feat: Initial ZK-Glasses project — IR anti-surveillance eyewear
Complete hardware design for glasses with flush-mounted 940nm IR LEDs that are invisible to human eyes but overwhelm camera sensors: - OpenSCAD parametric models (stealth integrated frame + snap-on clip) - SMD LEDs behind IR-pass filter strips (Wratten 87C style) - ATtiny85 PWM firmware (6 modes, brightness control, auto-off) - SVG circuit schematic (dark-themed, full driver circuit) - 10 rendered preview images (hero, front, cross-section, camera view) - KiCad project shell for future PCB layout Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
* ╔═══════════════════════════════════════════════════════════╗
|
||||
* ║ ZK-Glasses — ATtiny85 IR LED Controller Firmware ║
|
||||
* ╠═══════════════════════════════════════════════════════════╣
|
||||
* ║ Board: ATtiny85 (ATTinyCore, 8 MHz internal) ║
|
||||
* ║ Programmer: USBasp / Arduino as ISP ║
|
||||
* ║ License: MIT ║
|
||||
* ╚═══════════════════════════════════════════════════════════╝
|
||||
*
|
||||
* Pin Assignment (DIP-8):
|
||||
* ┌────────────────────────────┐
|
||||
* │ ATtiny85 │
|
||||
* │ (RST) PB5 ─┤1 8├─ VCC │
|
||||
* │ (ADC) PB3 ─┤2 7├─ PB2 (BTN)
|
||||
* │ (OC1B)PB4 ─┤3 6├─ PB1 (LED_R) OC0B
|
||||
* │ GND ─┤4 5├─ PB0 (LED_L) OC0A
|
||||
* └────────────────────────────┘
|
||||
*
|
||||
* PB0 - LED group LEFT (8 IR LEDs via N-MOSFET gate)
|
||||
* PB1 - LED group RIGHT (8 IR LEDs via N-MOSFET gate)
|
||||
* PB2 - Tactile button (internal pull-up, active LOW)
|
||||
* PB3 - Status LED (green, current-limited)
|
||||
* PB4 - Battery voltage sense (ADC2, voltage divider)
|
||||
*
|
||||
* Modes:
|
||||
* 0 OFF — LEDs off, MCU in low-power idle
|
||||
* 1 CONSTANT — Full brightness, steady
|
||||
* 2 PULSE_30HZ — 30 Hz square wave (half power draw)
|
||||
* 3 PULSE_60HZ — 60 Hz, matches common camera fps
|
||||
* 4 RANDOM_FLICKER — Pseudorandom on/off, defeats adaptive filters
|
||||
* 5 STEALTH — 25% duty constant (very low visibility)
|
||||
*
|
||||
* Controls:
|
||||
* Short press (<500ms) — Cycle to next mode
|
||||
* Long press (>800ms) — Cycle brightness (25→50→75→100%)
|
||||
* Double press (<300ms) — Instant OFF
|
||||
*
|
||||
* Features:
|
||||
* - Auto-off after 2 hours (configurable)
|
||||
* - Low battery warning: status LED blinks when Vcc < 3.3V
|
||||
* - All timing via Timer0 (no delay() in main loop)
|
||||
*/
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/sleep.h>
|
||||
#include <avr/wdt.h>
|
||||
|
||||
// ── Pin Definitions ──────────────────────────────────────────
|
||||
#define PIN_LED_L PB0 // Left IR LED group (OC0A)
|
||||
#define PIN_LED_R PB1 // Right IR LED group (OC0B)
|
||||
#define PIN_BTN PB2 // Button input
|
||||
#define PIN_STATUS PB3 // Status LED
|
||||
#define PIN_VBAT PB4 // Battery ADC (ADC2)
|
||||
|
||||
// ── Configuration ────────────────────────────────────────────
|
||||
#define NUM_MODES 6
|
||||
#define DEBOUNCE_MS 40
|
||||
#define SHORT_PRESS_MAX 500 // ms — below this = short press
|
||||
#define LONG_PRESS_MIN 800 // ms — above this = long press
|
||||
#define DOUBLE_PRESS_MAX 300 // ms — gap for double-press detection
|
||||
#define AUTO_OFF_MS 7200000UL // 2 hours in ms
|
||||
#define LOW_BATT_ADC 175 // ~3.3V via voltage divider (3.3/5*1024*R2/(R1+R2))
|
||||
#define BATT_CHECK_INTERVAL 10000 // Check battery every 10s
|
||||
|
||||
// ── Brightness Levels (PWM duty 0–255) ───────────────────────
|
||||
const uint8_t BRIGHTNESS_LEVELS[] = { 64, 128, 192, 255 };
|
||||
#define NUM_BRIGHTNESS 4
|
||||
|
||||
// ── Mode Enumeration ─────────────────────────────────────────
|
||||
enum Mode : uint8_t {
|
||||
MODE_OFF = 0,
|
||||
MODE_CONSTANT = 1,
|
||||
MODE_PULSE_30HZ = 2,
|
||||
MODE_PULSE_60HZ = 3,
|
||||
MODE_RANDOM = 4,
|
||||
MODE_STEALTH = 5
|
||||
};
|
||||
|
||||
// ── State ────────────────────────────────────────────────────
|
||||
volatile uint8_t g_mode = MODE_OFF;
|
||||
volatile uint8_t g_brightness_idx = 3; // Start at full
|
||||
volatile uint32_t g_millis = 0;
|
||||
volatile bool g_btn_pressed = false;
|
||||
|
||||
uint32_t g_mode_start_ms = 0; // For auto-off timer
|
||||
uint32_t g_last_batt_check = 0;
|
||||
bool g_low_battery = false;
|
||||
uint16_t g_lfsr = 0xACE1; // LFSR seed for random mode
|
||||
|
||||
// ── Millisecond Timer (Timer0 overflow at 8MHz/64/256 ≈ 488Hz) ──
|
||||
// We'll use Timer1 for millis since Timer0 is used for PWM
|
||||
// Actually, let's use a simple millis implementation with Timer0
|
||||
// Timer0 is set up for Fast PWM on OC0A/OC0B
|
||||
|
||||
// We'll track time using Timer1 overflow
|
||||
ISR(TIMER1_OVF_vect) {
|
||||
// Timer1 at 8MHz/64, 8-bit overflow = every 2.048ms
|
||||
// We'll count overflows for rough millisecond tracking
|
||||
g_millis += 2;
|
||||
}
|
||||
|
||||
// ── LFSR Pseudorandom (16-bit Galois) ────────────────────────
|
||||
uint16_t lfsr_next() {
|
||||
uint16_t bit = ((g_lfsr >> 0) ^ (g_lfsr >> 2) ^
|
||||
(g_lfsr >> 3) ^ (g_lfsr >> 5)) & 1u;
|
||||
g_lfsr = (g_lfsr >> 1) | (bit << 15);
|
||||
return g_lfsr;
|
||||
}
|
||||
|
||||
// ── ADC Read (blocking, 10-bit) ──────────────────────────────
|
||||
uint16_t read_adc(uint8_t channel) {
|
||||
ADMUX = channel; // Vcc reference, selected channel
|
||||
ADCSRA = (1 << ADEN) | (1 << ADSC) | // Enable, start conversion
|
||||
(1 << ADPS2) | (1 << ADPS1); // Prescaler /64
|
||||
while (ADCSRA & (1 << ADSC)); // Wait for completion
|
||||
uint16_t result = ADC;
|
||||
ADCSRA &= ~(1 << ADEN); // Disable ADC to save power
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Set LED PWM Duty Cycle ───────────────────────────────────
|
||||
void set_leds(uint8_t duty) {
|
||||
OCR0A = duty; // Left group
|
||||
OCR0B = duty; // Right group
|
||||
}
|
||||
|
||||
// ── Status LED Control ───────────────────────────────────────
|
||||
void status_led(bool on) {
|
||||
if (on) PORTB |= (1 << PIN_STATUS);
|
||||
else PORTB &= ~(1 << PIN_STATUS);
|
||||
}
|
||||
|
||||
// ── Button Reading (debounced) ───────────────────────────────
|
||||
bool button_is_down() {
|
||||
return !(PINB & (1 << PIN_BTN)); // Active LOW
|
||||
}
|
||||
|
||||
// ── Setup ────────────────────────────────────────────────────
|
||||
void setup() {
|
||||
// --- GPIO ---
|
||||
DDRB = (1 << PIN_LED_L) | (1 << PIN_LED_R) | (1 << PIN_STATUS);
|
||||
PORTB = (1 << PIN_BTN); // Pull-up on button
|
||||
|
||||
// --- Timer0: Fast PWM on OC0A (PB0) and OC0B (PB1) ---
|
||||
// Mode 3 (Fast PWM, TOP=0xFF), prescaler /1 for ~31.4 kHz base
|
||||
// Non-inverting output on both channels
|
||||
TCCR0A = (1 << COM0A1) | (1 << COM0B1) |
|
||||
(1 << WGM01) | (1 << WGM00);
|
||||
TCCR0B = (1 << CS00); // No prescaler → 8MHz/256 = 31.25kHz
|
||||
OCR0A = 0;
|
||||
OCR0B = 0;
|
||||
|
||||
// --- Timer1: Millis tracking ---
|
||||
// CTC mode, prescaler /64 → overflow at 8MHz/64/256 ≈ 488 Hz
|
||||
TCCR1 = (1 << CS12) | (1 << CS11) | (1 << CS10); // /64
|
||||
TIMSK |= (1 << TOIE1); // Overflow interrupt
|
||||
|
||||
sei(); // Enable global interrupts
|
||||
|
||||
// Startup flash
|
||||
status_led(true);
|
||||
_delay_ms(200);
|
||||
status_led(false);
|
||||
}
|
||||
|
||||
// ── Process Button Input ─────────────────────────────────────
|
||||
void process_button() {
|
||||
static uint32_t press_start = 0;
|
||||
static uint32_t last_release = 0;
|
||||
static bool was_pressed = false;
|
||||
static bool awaiting_double = false;
|
||||
static uint8_t press_count = 0;
|
||||
|
||||
bool pressed = button_is_down();
|
||||
uint32_t now = g_millis;
|
||||
|
||||
if (pressed && !was_pressed) {
|
||||
// --- Button just pressed ---
|
||||
press_start = now;
|
||||
was_pressed = true;
|
||||
}
|
||||
else if (!pressed && was_pressed) {
|
||||
// --- Button just released ---
|
||||
uint32_t duration = now - press_start;
|
||||
was_pressed = false;
|
||||
|
||||
if (duration > LONG_PRESS_MIN) {
|
||||
// Long press → cycle brightness
|
||||
g_brightness_idx = (g_brightness_idx + 1) % NUM_BRIGHTNESS;
|
||||
// Flash status LED to indicate level
|
||||
for (uint8_t i = 0; i <= g_brightness_idx; i++) {
|
||||
status_led(true);
|
||||
_delay_ms(80);
|
||||
status_led(false);
|
||||
_delay_ms(80);
|
||||
}
|
||||
}
|
||||
else if (duration < SHORT_PRESS_MAX) {
|
||||
// Short press — check for double press
|
||||
if (awaiting_double && (now - last_release < DOUBLE_PRESS_MAX)) {
|
||||
// Double press → OFF
|
||||
g_mode = MODE_OFF;
|
||||
set_leds(0);
|
||||
awaiting_double = false;
|
||||
press_count = 0;
|
||||
status_led(true);
|
||||
_delay_ms(50);
|
||||
status_led(false);
|
||||
return;
|
||||
}
|
||||
awaiting_double = true;
|
||||
last_release = now;
|
||||
press_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize single press after double-press window expires
|
||||
if (awaiting_double && !pressed &&
|
||||
(now - last_release > DOUBLE_PRESS_MAX)) {
|
||||
// Single short press → next mode
|
||||
g_mode = (g_mode + 1) % NUM_MODES;
|
||||
g_mode_start_ms = now;
|
||||
awaiting_double = false;
|
||||
press_count = 0;
|
||||
|
||||
// Quick status flash for mode feedback
|
||||
for (uint8_t i = 0; i <= g_mode; i++) {
|
||||
status_led(true);
|
||||
_delay_ms(40);
|
||||
status_led(false);
|
||||
_delay_ms(40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Battery Check ────────────────────────────────────────────
|
||||
void check_battery() {
|
||||
uint16_t adc_val = read_adc(2); // ADC2 = PB4
|
||||
g_low_battery = (adc_val < LOW_BATT_ADC);
|
||||
}
|
||||
|
||||
// ── Main Loop ────────────────────────────────────────────────
|
||||
void loop() {
|
||||
uint32_t now = g_millis;
|
||||
|
||||
// --- Button handling ---
|
||||
process_button();
|
||||
|
||||
// --- Auto-off timer ---
|
||||
if (g_mode != MODE_OFF &&
|
||||
(now - g_mode_start_ms > AUTO_OFF_MS)) {
|
||||
g_mode = MODE_OFF;
|
||||
set_leds(0);
|
||||
}
|
||||
|
||||
// --- Battery check (periodic) ---
|
||||
if (now - g_last_batt_check > BATT_CHECK_INTERVAL) {
|
||||
g_last_batt_check = now;
|
||||
check_battery();
|
||||
}
|
||||
|
||||
// --- Low battery warning ---
|
||||
if (g_low_battery && g_mode != MODE_OFF) {
|
||||
// Blink status LED every 2 seconds
|
||||
status_led((now / 500) % 4 == 0);
|
||||
}
|
||||
|
||||
// --- LED Mode Execution ---
|
||||
uint8_t duty = BRIGHTNESS_LEVELS[g_brightness_idx];
|
||||
|
||||
switch (g_mode) {
|
||||
|
||||
case MODE_OFF:
|
||||
set_leds(0);
|
||||
status_led(false);
|
||||
// Could enter sleep mode here for power savings
|
||||
break;
|
||||
|
||||
case MODE_CONSTANT:
|
||||
set_leds(duty);
|
||||
break;
|
||||
|
||||
case MODE_PULSE_30HZ:
|
||||
// 30 Hz = 33.3ms period, 16.7ms on / 16.7ms off
|
||||
if ((now % 33) < 17)
|
||||
set_leds(duty);
|
||||
else
|
||||
set_leds(0);
|
||||
break;
|
||||
|
||||
case MODE_PULSE_60HZ:
|
||||
// 60 Hz = 16.7ms period, 8.3ms on / 8.3ms off
|
||||
if ((now % 17) < 9)
|
||||
set_leds(duty);
|
||||
else
|
||||
set_leds(0);
|
||||
break;
|
||||
|
||||
case MODE_RANDOM:
|
||||
// Change state every 5–25ms (pseudorandom)
|
||||
{
|
||||
static uint32_t next_change = 0;
|
||||
if (now >= next_change) {
|
||||
uint16_t rnd = lfsr_next();
|
||||
// Random on/off
|
||||
if (rnd & 1)
|
||||
set_leds(duty);
|
||||
else
|
||||
set_leds(0);
|
||||
// Random interval 5–25ms
|
||||
next_change = now + 5 + (rnd % 21);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MODE_STEALTH:
|
||||
// 25% of selected brightness, constant
|
||||
set_leds(duty / 4);
|
||||
break;
|
||||
}
|
||||
|
||||
_delay_ms(1); // ~1 kHz loop rate
|
||||
}
|
||||
|
||||
/*
|
||||
* ── Build & Flash Notes ──────────────────────────────────────
|
||||
*
|
||||
* Arduino IDE Setup:
|
||||
* 1. Install ATTinyCore via Board Manager
|
||||
* URL: http://drazzy.com/package_drazzy.com_index.json
|
||||
* 2. Board: "ATtiny25/45/85 (No bootloader)"
|
||||
* 3. Chip: ATtiny85
|
||||
* 4. Clock: 8 MHz (internal)
|
||||
* 5. Programmer: USBasp (or "Arduino as ISP")
|
||||
* 6. Burn Bootloader first (sets fuses)
|
||||
* 7. Upload with programmer (Ctrl+Shift+U)
|
||||
*
|
||||
* PlatformIO (alternative):
|
||||
* [env:attiny85]
|
||||
* platform = atmelavr
|
||||
* board = attiny85
|
||||
* framework = arduino
|
||||
* board_build.f_cpu = 8000000L
|
||||
* upload_protocol = usbasp
|
||||
*
|
||||
* Power Consumption (estimated):
|
||||
* Mode | Avg Draw | Runtime (500mAh)
|
||||
* ──────────────┼───────────┼─────────────────
|
||||
* OFF | ~5 µA | years
|
||||
* CONSTANT 100% | ~1.6 A | ~18 min
|
||||
* PULSE 30Hz | ~800 mA | ~37 min
|
||||
* PULSE 60Hz | ~800 mA | ~37 min
|
||||
* RANDOM | ~600 mA | ~50 min
|
||||
* STEALTH | ~400 mA | ~75 min
|
||||
*
|
||||
* (16 LEDs × 100mA = 1.6A max; MCU + driver overhead ~5mA)
|
||||
*
|
||||
* Wiring Quick Reference:
|
||||
*
|
||||
* ATtiny85 Pin 5 (PB0) ──→ 1kΩ ──→ Q1 Gate (IRLML6344)
|
||||
* Q1 Drain ──→ LED Group L (8× parallel)
|
||||
* Q1 Source ──→ GND
|
||||
*
|
||||
* ATtiny85 Pin 6 (PB1) ──→ 1kΩ ──→ Q2 Gate (IRLML6344)
|
||||
* Q2 Drain ──→ LED Group R (8× parallel)
|
||||
* Q2 Source ──→ GND
|
||||
*
|
||||
* ATtiny85 Pin 7 (PB2) ──→ Button ──→ GND (internal pull-up)
|
||||
* ATtiny85 Pin 2 (PB3) ──→ 330Ω ──→ Status LED ──→ GND
|
||||
* ATtiny85 Pin 3 (PB4) ──→ Voltage divider (100k/100k) ──→ VBAT
|
||||
*/
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
// ============================================================
|
||||
// ZK-Glasses — Snap-On IR Clip Module
|
||||
// Clips onto any existing glasses temple arm
|
||||
// ============================================================
|
||||
//
|
||||
// ┌──────────────────────────────────────────────────────┐
|
||||
// │ RENDER INSTRUCTIONS │
|
||||
// │ F5 = Quick preview (colors, transparency) │
|
||||
// │ F6 = Full render (slow, for STL export) │
|
||||
// │ F7 = Export STL │
|
||||
// │ │
|
||||
// │ Toggle variables below for different views: │
|
||||
// │ • show_beams = IR beam visualization │
|
||||
// │ • show_temple = phantom glasses arm │
|
||||
// │ • exploded = exploded assembly view │
|
||||
// │ • cross_section = cutaway internal view │
|
||||
// │ • show_electronics = ATtiny + MOSFETs + wiring │
|
||||
// └──────────────────────────────────────────────────────┘
|
||||
|
||||
include <common.scad>
|
||||
|
||||
// ── View Toggles ─────────────────────────────────────────────
|
||||
show_beams = true; // Show purple IR beam cones
|
||||
show_temple = true; // Show transparent phantom glasses arm
|
||||
exploded = false; // Exploded assembly view
|
||||
cross_section = false; // Cutaway to show internals
|
||||
show_electronics = true; // Show ATtiny, MOSFETs, wires
|
||||
|
||||
// ── Glasses Temple Parameters (measure yours!) ───────────────
|
||||
temple_w = 5.0; // Width of your glasses temple arm
|
||||
temple_h = 2.8; // Thickness of temple arm
|
||||
temple_len = 140; // Length (for phantom display)
|
||||
|
||||
// ── Clip Parameters ──────────────────────────────────────────
|
||||
clip_len = 52; // Length of main clip body
|
||||
clip_wall = 2.0; // Shell thickness
|
||||
clip_tol = 0.4; // Tolerance around temple
|
||||
grip_lip = 0.6; // Inward lip for snap retention
|
||||
|
||||
// ── LED Array ────────────────────────────────────────────────
|
||||
led_count = 5; // LEDs along top of each clip
|
||||
led_pitch = 9.0; // Center-to-center spacing
|
||||
led_dia = 3.0; // 3mm LEDs
|
||||
led_fwd_tilt = 12; // Forward tilt angle (toward cameras)
|
||||
led_out_tilt = 5; // Slight outward splay
|
||||
|
||||
// ── Battery Pod ──────────────────────────────────────────────
|
||||
batt_l = 38; // Battery compartment length
|
||||
batt_w = 22; // Battery compartment width
|
||||
batt_h = 8; // Battery compartment height
|
||||
|
||||
// ── Bridge Connector ─────────────────────────────────────────
|
||||
bridge_w = 3.0; // Width of brow connector strip
|
||||
bridge_h = 2.5; // Height of connector
|
||||
|
||||
// ── Derived ──────────────────────────────────────────────────
|
||||
clip_inner_w = temple_w + clip_tol * 2;
|
||||
clip_inner_h = temple_h + clip_tol * 2;
|
||||
clip_outer_w = clip_inner_w + clip_wall * 2;
|
||||
clip_outer_h = clip_inner_h + clip_wall + 5; // extra height above for LEDs
|
||||
ex = exploded ? 25 : 0; // exploded offset
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// CLIP BODY — main structural piece
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
module clip_body() {
|
||||
difference() {
|
||||
// Outer shell — rounded block
|
||||
minkowski() {
|
||||
cube([clip_len - 2, clip_outer_w - 2, clip_outer_h - 1],
|
||||
center = true);
|
||||
sphere(r = 1.0, $fn = 16);
|
||||
}
|
||||
|
||||
// Temple channel (open at bottom for snap-on)
|
||||
translate([0, 0, -1])
|
||||
cube([clip_len + 2, clip_inner_w, clip_inner_h + 2],
|
||||
center = true);
|
||||
|
||||
// Bottom slot opening (narrower than channel for snap fit)
|
||||
translate([0, 0, -(clip_outer_h/2)])
|
||||
cube([clip_len + 2, clip_inner_w - grip_lip * 2, clip_wall + 2],
|
||||
center = true);
|
||||
|
||||
// LED sockets (angled holes through top)
|
||||
for (i = [0 : led_count - 1]) {
|
||||
x = -(led_pitch * (led_count - 1) / 2) + i * led_pitch;
|
||||
translate([x, 0, clip_outer_h / 2 - 1])
|
||||
rotate([-led_fwd_tilt, 0, 0])
|
||||
cylinder(d = led_dia + 0.4, h = 12, $fn = 24);
|
||||
}
|
||||
|
||||
// Wire channel (horizontal bore through body)
|
||||
translate([0, 0, clip_outer_h / 2 - 3.5])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = 2.0, h = clip_len + 4, center = true, $fn = 16);
|
||||
|
||||
// Wire exit holes at each end
|
||||
for (sx = [-1, 1])
|
||||
translate([sx * (clip_len/2 + 0.5), 0, clip_outer_h / 2 - 3.5])
|
||||
sphere(d = 3.0, $fn = 16);
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// BATTERY POD — sits behind the ear
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
module battery_pod() {
|
||||
difference() {
|
||||
// Outer shell
|
||||
minkowski() {
|
||||
cube([batt_l - 3, batt_w - 3, batt_h - 1], center = true);
|
||||
sphere(r = 1.5, $fn = 16);
|
||||
}
|
||||
// Battery cavity
|
||||
translate([0, 0, 1])
|
||||
cube([batt_l - 4, batt_w - 4, batt_h], center = true);
|
||||
|
||||
// Charge port cutout (USB-C, one end)
|
||||
translate([-(batt_l/2), 0, 0])
|
||||
rotate([0, 90, 0])
|
||||
rounded_rect_extrude(9.5, 3.5, 1.0, 6);
|
||||
|
||||
// Wire exit (connects to clip)
|
||||
translate([batt_l/2, 0, batt_h/2 - 2])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = 2.5, h = 4, center = true, $fn = 16);
|
||||
|
||||
// Slide switch cutout (top)
|
||||
translate([-8, 0, batt_h/2])
|
||||
cube([10, 4.5, 4], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper for charge port cutout
|
||||
module rounded_rect_extrude(w, h, r, depth) {
|
||||
linear_extrude(depth)
|
||||
offset(r = r) offset(delta = -r)
|
||||
square([w - 2*r, h - 2*r], center = true);
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// BROW BRIDGE — connects left and right clips
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
module brow_bridge(span = 20) {
|
||||
// Flexible strip that goes across the nose bridge area
|
||||
color(CLR_FRAME)
|
||||
difference() {
|
||||
minkowski() {
|
||||
cube([span, bridge_w - 1, bridge_h - 0.5], center = true);
|
||||
sphere(r = 0.5, $fn = 12);
|
||||
}
|
||||
// Wire channel
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = 1.5, h = span + 4, center = true, $fn = 12);
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// FULL ASSEMBLY — single side (mirrored for both)
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
module clip_assembly_side(side = "left") {
|
||||
mirror_x = (side == "right") ? 1 : 0;
|
||||
|
||||
mirror([mirror_x, 0, 0]) {
|
||||
// ── Clip body ──
|
||||
translate([0, 0, ex])
|
||||
color(CLR_FRAME)
|
||||
clip_body();
|
||||
|
||||
// ── LEDs in sockets ──
|
||||
for (i = [0 : led_count - 1]) {
|
||||
x = -(led_pitch * (led_count - 1) / 2) + i * led_pitch;
|
||||
translate([x, 0, clip_outer_h / 2 - 1 + ex * 1.5])
|
||||
rotate([-led_fwd_tilt, 0, 0])
|
||||
ir_led_3mm(show_beam = show_beams);
|
||||
}
|
||||
|
||||
// ── Battery pod (behind clip) ──
|
||||
translate([clip_len/2 + batt_l/2 + 3, 0, -ex * 0.3])
|
||||
color(CLR_FRAME)
|
||||
battery_pod();
|
||||
|
||||
// ── Battery inside pod ──
|
||||
translate([clip_len/2 + batt_l/2 + 3, 0, 1 - ex * 0.1])
|
||||
lipo_battery(l = batt_l - 6, w = batt_w - 6, h = batt_h - 4);
|
||||
|
||||
// ── Slide switch ──
|
||||
translate([clip_len/2 + batt_l/2 + 3 - 8, 0, batt_h/2 + 0.5 - ex * 0.2])
|
||||
slide_switch();
|
||||
|
||||
// ── Phantom temple arm ──
|
||||
if (show_temple)
|
||||
translate([20, 0, 0])
|
||||
phantom_temple(length = temple_len, width = temple_w, height = temple_h);
|
||||
|
||||
// ── Electronics (inside clip body) ──
|
||||
if (show_electronics) {
|
||||
// ATtiny85
|
||||
translate([10, 0, clip_outer_h / 2 - 5 + ex * 0.8])
|
||||
rotate([0, 0, 90])
|
||||
attiny85_dip8();
|
||||
|
||||
// MOSFETs
|
||||
translate([-5, 2, clip_outer_h / 2 - 5 + ex * 0.6])
|
||||
mosfet_sot23();
|
||||
translate([-5, -2, clip_outer_h / 2 - 5 + ex * 0.6])
|
||||
mosfet_sot23();
|
||||
|
||||
// Button (on outer face of clip)
|
||||
translate([-15, clip_outer_w/2 + 1, 0])
|
||||
rotate([90, 0, 0])
|
||||
tact_button();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// MAIN ASSEMBLY
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
// Left clip
|
||||
translate([-40, 0, 0])
|
||||
clip_assembly_side("left");
|
||||
|
||||
// Right clip
|
||||
translate([40, 0, 0])
|
||||
clip_assembly_side("right");
|
||||
|
||||
// Brow bridge connector
|
||||
translate([0, 0, clip_outer_h / 2 + ex * 0.8])
|
||||
brow_bridge(span = 60);
|
||||
|
||||
// ── Optional cross section ──
|
||||
if (cross_section) {
|
||||
// Cut away front half to show internals
|
||||
translate([0, 50, 0])
|
||||
cube([200, 100, 100], center = true);
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// PRINTABLE PARTS (uncomment one at a time for STL export)
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
// For 3D printing, render each part flat on the build plate:
|
||||
|
||||
// !clip_body(); // Print 2×
|
||||
// !battery_pod(); // Print 2×
|
||||
// !brow_bridge(span = 60); // Print 1×
|
||||
|
||||
// Print settings:
|
||||
// Material: PETG (flexibility + heat resistance)
|
||||
// Layer height: 0.16mm
|
||||
// Infill: 30% gyroid
|
||||
// Supports: Yes (for LED socket overhangs)
|
||||
// Wall count: 3
|
||||
// Brim: Yes (small footprint parts)
|
||||
|
|
@ -0,0 +1,355 @@
|
|||
// ============================================================
|
||||
// ZK-Glasses — Common Components Library
|
||||
// Shared modules for clip-on and integrated frame variants
|
||||
// ============================================================
|
||||
//
|
||||
// Usage: include <common.scad>
|
||||
//
|
||||
// All dimensions in millimeters.
|
||||
// Default preview resolution — bump to 128 for final render.
|
||||
|
||||
$fn = 64;
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// Color palette (consistent across all files)
|
||||
// ──────────────────────────────────────────────
|
||||
CLR_FRAME = [0.22, 0.22, 0.24]; // matte charcoal
|
||||
CLR_LED_BODY = [0.12, 0.08, 0.18, 0.7]; // dark tinted epoxy
|
||||
CLR_LED_BEAM = [0.55, 0.0, 1.0, 0.06]; // faint purple IR viz
|
||||
CLR_SILVER = [0.78, 0.78, 0.80];
|
||||
CLR_GOLD = [0.83, 0.69, 0.22];
|
||||
CLR_PCB = [0.05, 0.35, 0.12];
|
||||
CLR_BATTERY = [0.30, 0.50, 0.72];
|
||||
CLR_RUBBER = [0.15, 0.15, 0.15];
|
||||
CLR_WIRE_R = [0.7, 0.1, 0.1];
|
||||
CLR_WIRE_B = [0.1, 0.1, 0.1];
|
||||
CLR_IR_FILTER= [0.08, 0.04, 0.10, 0.92]; // near-black IR-pass filter
|
||||
CLR_FLEX_PCB = [0.15, 0.12, 0.02]; // dark gold flex PCB
|
||||
CLR_SMD_LED = [0.10, 0.10, 0.12]; // tiny dark SMD body
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 2D helper — rounded rectangle
|
||||
// ──────────────────────────────────────────────
|
||||
module rounded_rect_2d(w, h, r) {
|
||||
offset(r = r)
|
||||
offset(delta = -r)
|
||||
square([w, h], center = true);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// SMD IR LED — OSRAM SFH 4726AS style
|
||||
// 940nm, PLCC-2 (3.5 × 2.8 × 1.9 mm)
|
||||
// Flush-mountable, nearly invisible in frame
|
||||
// ──────────────────────────────────────────────
|
||||
module ir_led_smd(show_beam = false) {
|
||||
// Tiny SMD body — barely visible
|
||||
color(CLR_SMD_LED) {
|
||||
cube([3.5, 2.8, 1.9], center = true);
|
||||
// Lens window (top face, recessed)
|
||||
color([0.06, 0.03, 0.08, 0.85])
|
||||
translate([0, 0, 0.9])
|
||||
cube([2.8, 2.0, 0.15], center = true);
|
||||
}
|
||||
// Solder pads
|
||||
color(CLR_SILVER) {
|
||||
translate([-1.4, 0, -0.95])
|
||||
cube([0.8, 2.0, 0.1], center = true);
|
||||
translate([ 1.4, 0, -0.95])
|
||||
cube([0.8, 2.0, 0.1], center = true);
|
||||
}
|
||||
// IR beam — wider angle than through-hole (±60°)
|
||||
if (show_beam) {
|
||||
color([0.55, 0.0, 1.0, 0.04])
|
||||
translate([0, 0, 1.5])
|
||||
cylinder(d1 = 2, d2 = 80, h = 60, $fn = 32);
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// Flex PCB LED Strip
|
||||
// Thin flexible circuit with SMD LEDs pre-soldered
|
||||
// Slides into channel inside the brow bar
|
||||
// ──────────────────────────────────────────────
|
||||
module flex_pcb_strip(length = 100, led_count = 6, led_pitch = 12) {
|
||||
// Flex substrate (0.2mm thick, like a ribbon cable)
|
||||
color(CLR_FLEX_PCB)
|
||||
cube([length, 4.0, 0.2], center = true);
|
||||
|
||||
// Copper traces (decorative)
|
||||
color([0.72, 0.55, 0.15], 0.3)
|
||||
for (dy = [-1, 1])
|
||||
translate([0, dy * 1.2, 0.12])
|
||||
cube([length - 2, 0.4, 0.04], center = true);
|
||||
|
||||
// SMD LEDs on the strip
|
||||
for (i = [0 : led_count - 1]) {
|
||||
x = -(led_pitch * (led_count - 1) / 2) + i * led_pitch;
|
||||
translate([x, 0, 1.05])
|
||||
ir_led_smd(show_beam = false);
|
||||
}
|
||||
|
||||
// Connector tail (one end)
|
||||
color(CLR_FLEX_PCB)
|
||||
translate([length/2 + 5, 0, 0])
|
||||
cube([12, 3, 0.2], center = true);
|
||||
// Connector pins
|
||||
color(CLR_GOLD)
|
||||
translate([length/2 + 10, 0, 0])
|
||||
cube([2, 4, 0.8], center = true);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// IR-Pass Filter Strip
|
||||
// Looks opaque black to human eyes, but
|
||||
// transmits 940nm IR light freely.
|
||||
// (Kodak Wratten 87C equivalent)
|
||||
// ──────────────────────────────────────────────
|
||||
module ir_filter_strip(length = 100, width = 4.0, thickness = 0.5) {
|
||||
color(CLR_IR_FILTER)
|
||||
cube([length, width, thickness], center = true);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 3mm Through-Hole IR LED (TSAL6200 style)
|
||||
// ──────────────────────────────────────────────
|
||||
module ir_led_3mm(show_beam = false) {
|
||||
// Epoxy body + dome
|
||||
color(CLR_LED_BODY) {
|
||||
cylinder(d = 3.0, h = 4.5);
|
||||
translate([0, 0, 4.5])
|
||||
sphere(d = 3.0);
|
||||
// Flange
|
||||
cylinder(d = 3.6, h = 0.8);
|
||||
}
|
||||
// Anode / cathode leads
|
||||
color(CLR_SILVER) {
|
||||
translate([ 1.27/2, 0, -6])
|
||||
cylinder(d = 0.45, h = 6, $fn = 8);
|
||||
translate([-1.27/2, 0, -6])
|
||||
cylinder(d = 0.45, h = 6, $fn = 8);
|
||||
}
|
||||
// IR beam cone (visual only)
|
||||
if (show_beam) {
|
||||
color(CLR_LED_BEAM)
|
||||
translate([0, 0, 5.5])
|
||||
cylinder(d1 = 2.5, d2 = 45, h = 70);
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// 5mm Through-Hole IR LED
|
||||
// ──────────────────────────────────────────────
|
||||
module ir_led_5mm(show_beam = false) {
|
||||
color(CLR_LED_BODY) {
|
||||
cylinder(d = 5.0, h = 6.0);
|
||||
translate([0, 0, 6.0])
|
||||
sphere(d = 5.0);
|
||||
cylinder(d = 5.8, h = 1.0);
|
||||
}
|
||||
color(CLR_SILVER) {
|
||||
translate([ 1.27, 0, -8])
|
||||
cylinder(d = 0.45, h = 8, $fn = 8);
|
||||
translate([-1.27, 0, -8])
|
||||
cylinder(d = 0.45, h = 8, $fn = 8);
|
||||
}
|
||||
if (show_beam) {
|
||||
color(CLR_LED_BEAM)
|
||||
translate([0, 0, 7.0])
|
||||
cylinder(d1 = 4, d2 = 55, h = 80);
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// LiPo Battery Cell
|
||||
// ──────────────────────────────────────────────
|
||||
module lipo_battery(l = 35, w = 20, h = 5.5) {
|
||||
color(CLR_BATTERY)
|
||||
minkowski() {
|
||||
cube([l - 2, w - 2, h - 1], center = true);
|
||||
sphere(r = 0.5, $fn = 16);
|
||||
}
|
||||
// JST connector tab
|
||||
color(CLR_GOLD)
|
||||
translate([l/2 + 1, 0, 0])
|
||||
cube([3, 5, 1.2], center = true);
|
||||
// Label
|
||||
color("White")
|
||||
translate([0, 0, h/2 + 0.01])
|
||||
linear_extrude(0.1)
|
||||
text("3.7V 500mAh", size = 2.5,
|
||||
halign = "center", valign = "center");
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// TP4056 USB-C Charge Board (25 × 14 mm)
|
||||
// ──────────────────────────────────────────────
|
||||
module tp4056_board() {
|
||||
// PCB
|
||||
color(CLR_PCB) {
|
||||
cube([25.4, 14.2, 1.2], center = true);
|
||||
// Copper pads (bottom hint)
|
||||
translate([0, 0, -0.7])
|
||||
cube([24, 13, 0.05], center = true);
|
||||
}
|
||||
// USB-C receptacle
|
||||
color(CLR_SILVER)
|
||||
translate([-12.2, 0, 1.0])
|
||||
minkowski() {
|
||||
cube([5, 7.5, 2.0], center = true);
|
||||
sphere(r = 0.3, $fn = 12);
|
||||
}
|
||||
// TP4056 IC
|
||||
color("DimGray")
|
||||
translate([3, 0, 0.9])
|
||||
cube([5, 4, 1.2], center = true);
|
||||
// Inductor
|
||||
color([0.25, 0.25, 0.25])
|
||||
translate([9, 0, 1.0])
|
||||
cylinder(d = 4, h = 2, center = true, $fn = 16);
|
||||
// Charging LED (red)
|
||||
color("Red", 0.8)
|
||||
translate([0, 5.5, 0.8])
|
||||
cube([1.6, 0.8, 0.6], center = true);
|
||||
// Done LED (green)
|
||||
color("Lime", 0.8)
|
||||
translate([0, -5.5, 0.8])
|
||||
cube([1.6, 0.8, 0.6], center = true);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// ATtiny85 — DIP-8 Package
|
||||
// ──────────────────────────────────────────────
|
||||
module attiny85_dip8() {
|
||||
// Body
|
||||
color([0.18, 0.18, 0.20]) {
|
||||
cube([9.6, 6.35, 3.3], center = true);
|
||||
// Orientation notch
|
||||
translate([-4.8, 0, 1.65])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = 2.0, h = 0.5, $fn = 16);
|
||||
}
|
||||
// Dot marker
|
||||
color("White")
|
||||
translate([-3.5, -2.0, 1.66])
|
||||
cylinder(d = 0.8, h = 0.05, $fn = 12);
|
||||
// Pins (4 per side, 2.54 mm pitch)
|
||||
color(CLR_SILVER)
|
||||
for (i = [0:3]) {
|
||||
// Bottom pins
|
||||
translate([-3.81 + i * 2.54, -3.8, -1.2])
|
||||
cube([0.5, 1.5, 0.25]);
|
||||
// Top pins
|
||||
translate([-3.81 + i * 2.54, 2.3, -1.2])
|
||||
cube([0.5, 1.5, 0.25]);
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// N-MOSFET — SOT-23 Package (IRLML6344)
|
||||
// ──────────────────────────────────────────────
|
||||
module mosfet_sot23() {
|
||||
color([0.18, 0.18, 0.20])
|
||||
cube([2.9, 1.6, 1.1], center = true);
|
||||
color(CLR_SILVER) {
|
||||
// Gate, Source (bottom side)
|
||||
translate([-0.95, -1.3, 0])
|
||||
cube([0.4, 0.8, 0.15], center = true);
|
||||
translate([ 0.95, -1.3, 0])
|
||||
cube([0.4, 0.8, 0.15], center = true);
|
||||
// Drain (top side)
|
||||
translate([0, 1.3, 0])
|
||||
cube([0.4, 0.8, 0.15], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// Tactile Push Button (6 × 6 mm)
|
||||
// ──────────────────────────────────────────────
|
||||
module tact_button() {
|
||||
color([0.15, 0.15, 0.17]) {
|
||||
cube([6, 6, 3.5], center = true);
|
||||
}
|
||||
// Button cap
|
||||
color([0.3, 0.3, 0.32])
|
||||
translate([0, 0, 2.0])
|
||||
cylinder(d = 3.5, h = 1.0, $fn = 24);
|
||||
// Pins
|
||||
color(CLR_SILVER)
|
||||
for (dx = [-3.25, 3.25])
|
||||
for (dy = [-2.25, 2.25])
|
||||
translate([dx, dy, -2.5])
|
||||
cube([0.6, 0.6, 1.5], center = true);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// Slide Switch (8 × 3 mm)
|
||||
// ──────────────────────────────────────────────
|
||||
module slide_switch() {
|
||||
color([0.12, 0.12, 0.14])
|
||||
cube([8.5, 3.6, 3.5], center = true);
|
||||
// Slider knob
|
||||
color(CLR_SILVER)
|
||||
translate([1.5, 0, 2.0])
|
||||
cube([3, 1.5, 1.2], center = true);
|
||||
// Pins
|
||||
color(CLR_SILVER)
|
||||
for (dx = [-2.54, 0, 2.54])
|
||||
translate([dx, 0, -2.5])
|
||||
cylinder(d = 0.8, h = 2, $fn = 8);
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// Wire Channel (hull between point list)
|
||||
// ──────────────────────────────────────────────
|
||||
module wire_channel(pts, d = 1.5) {
|
||||
for (i = [0 : len(pts) - 2])
|
||||
hull() {
|
||||
translate(pts[i]) sphere(d = d, $fn = 12);
|
||||
translate(pts[i+1]) sphere(d = d, $fn = 12);
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// Nose Pad (soft rubber)
|
||||
// ──────────────────────────────────────────────
|
||||
module nose_pad() {
|
||||
color(CLR_RUBBER)
|
||||
minkowski() {
|
||||
scale([1, 0.6, 1])
|
||||
sphere(d = 8, $fn = 24);
|
||||
cube([0.5, 0.5, 0.5], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// Barrel Hinge (5-barrel, 2.5 mm pin)
|
||||
// ──────────────────────────────────────────────
|
||||
module barrel_hinge(open_angle = 0) {
|
||||
barrel_d = 3.0;
|
||||
barrel_h = 2.8;
|
||||
pin_d = 1.2;
|
||||
|
||||
color(CLR_SILVER) {
|
||||
// 5 interlocking barrels
|
||||
for (i = [0:4])
|
||||
translate([0, 0, i * barrel_h])
|
||||
cylinder(d = barrel_d, h = barrel_h - 0.2, $fn = 20);
|
||||
// Pin
|
||||
translate([0, 0, -0.5])
|
||||
cylinder(d = pin_d, h = 5 * barrel_h + 1, $fn = 12);
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────────────────────────────────────
|
||||
// Quick phantom glasses temple (for context)
|
||||
// ──────────────────────────────────────────────
|
||||
module phantom_temple(length = 140, width = 5, height = 2.5) {
|
||||
color([0.6, 0.6, 0.65], 0.3) {
|
||||
// Straight section
|
||||
cube([length, width, height], center = true);
|
||||
// Ear hook curve
|
||||
translate([length/2, 0, 0])
|
||||
rotate([0, -25, 0])
|
||||
cube([30, width, height], center = true);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,574 @@
|
|||
// ============================================================
|
||||
// ZK-Glasses — Stealth Integrated IR Frame
|
||||
// Flush-mounted SMD LEDs behind IR-pass filter strips
|
||||
// Looks like normal glasses — invisible except to cameras
|
||||
// ============================================================
|
||||
//
|
||||
// Design philosophy:
|
||||
// Like Meta Ray-Ban cameras — tiny components hidden in
|
||||
// the frame's natural geometry. A thin IR-pass filter strip
|
||||
// runs along the brow bar, looking like a decorative dark
|
||||
// accent line. Behind it: SMD IR LEDs on a flex PCB.
|
||||
//
|
||||
// ┌──────────────────────────────────────────────────────┐
|
||||
// │ F5 = Preview (fast, with colors + beams) │
|
||||
// │ F6 = Render (slow, for STL export) │
|
||||
// │ Customizer panel for real-time parameter tweaks │
|
||||
// └──────────────────────────────────────────────────────┘
|
||||
|
||||
include <common.scad>
|
||||
|
||||
// ╔═══════════════════════════════════════════════════════════╗
|
||||
// ║ VIEW CONTROLS ║
|
||||
// ╚═══════════════════════════════════════════════════════════╝
|
||||
|
||||
/* [View] */
|
||||
show_beams = true; // IR beam visualization (camera view)
|
||||
show_filter = true; // IR-pass filter strip overlay
|
||||
show_flex_pcb = true; // Internal flex PCB + SMD LEDs
|
||||
show_internals = true; // Battery, MCU, etc.
|
||||
cross_section = false; // Cutaway showing internal channels
|
||||
exploded = false; // Exploded assembly view
|
||||
print_layout = false; // Flat print-ready parts
|
||||
camera_view = false; // Simulate how a camera sees it (bright beams)
|
||||
|
||||
// ╔═══════════════════════════════════════════════════════════╗
|
||||
// ║ FACE MEASUREMENTS ║
|
||||
// ╚═══════════════════════════════════════════════════════════╝
|
||||
|
||||
/* [Face Dimensions] */
|
||||
face_width = 142; // Total frame width
|
||||
lens_width = 50; // Lens opening width
|
||||
lens_height = 36; // Lens opening height
|
||||
lens_corner_r = 9; // Corner radius (↑ = rounder)
|
||||
bridge_gap = 18; // Nose bridge gap
|
||||
nose_bridge_drop = 5; // Bridge drop below lens center
|
||||
|
||||
/* [Temple Arms] */
|
||||
temple_length = 140; // Temple arm length
|
||||
temple_width = 5.5; // Temple width
|
||||
temple_thickness = 4.0; // Temple thickness
|
||||
temple_angle = 8; // Outward splay
|
||||
ear_bend_angle = 22; // Ear hook curl
|
||||
ear_bend_start = 108; // Where bend begins
|
||||
|
||||
// ╔═══════════════════════════════════════════════════════════╗
|
||||
// ║ FRAME STRUCTURE ║
|
||||
// ╚═══════════════════════════════════════════════════════════╝
|
||||
|
||||
/* [Frame] */
|
||||
brow_thickness = 8.0; // Brow bar height (houses LED channel)
|
||||
rim_thickness = 3.5; // Lower/side rim
|
||||
frame_depth = 7.0; // Front-to-back depth
|
||||
frame_fillet = 1.2; // Edge rounding
|
||||
|
||||
// ╔═══════════════════════════════════════════════════════════╗
|
||||
// ║ STEALTH LED CONFIGURATION ║
|
||||
// ╚═══════════════════════════════════════════════════════════╝
|
||||
|
||||
/* [IR LEDs (Stealth)] */
|
||||
led_count_brow = 4; // SMD LEDs per side along brow
|
||||
led_count_bridge = 1; // Bridge LED (0 or 1)
|
||||
led_count_temple = 2; // LEDs per temple arm
|
||||
led_pitch = 10.0; // Brow LED center-to-center
|
||||
temple_led_pitch = 8.0; // Temple LED spacing
|
||||
|
||||
// Filter strip dimensions
|
||||
filter_width = 3.5; // Height of the IR filter window
|
||||
filter_depth = 0.6; // Filter material thickness
|
||||
filter_recess = 0.3; // How deep filter sits into frame
|
||||
|
||||
// Internal LED channel
|
||||
channel_height = 4.5; // Internal cavity height
|
||||
channel_depth = 5.0; // Internal cavity depth
|
||||
|
||||
// ╔═══════════════════════════════════════════════════════════╗
|
||||
// ║ BATTERY & ELECTRONICS ║
|
||||
// ╚═══════════════════════════════════════════════════════════╝
|
||||
|
||||
/* [Battery] */
|
||||
batt_length = 30;
|
||||
batt_width = 10;
|
||||
batt_thickness = 4.0;
|
||||
|
||||
// ── Derived ──────────────────────────────────────────────────
|
||||
total_leds = (led_count_brow + led_count_temple) * 2 + led_count_bridge;
|
||||
half_bridge = bridge_gap / 2;
|
||||
lens_cx = half_bridge + lens_width / 2;
|
||||
brow_y = lens_height / 2 + brow_thickness / 2;
|
||||
filter_y = lens_height / 2 + brow_thickness * 0.35; // centered in brow
|
||||
ex = exploded ? 30 : 0;
|
||||
|
||||
// Total brow LED span (for filter strip length)
|
||||
brow_led_span = led_pitch * (led_count_brow - 1);
|
||||
filter_length_side = brow_led_span + led_pitch; // per side
|
||||
filter_length_total = face_width - 12; // nearly full width
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// FRAME FRONT — 2D profile
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
module frame_front_2d() {
|
||||
difference() {
|
||||
union() {
|
||||
// Left lens surround
|
||||
translate([-lens_cx, 0])
|
||||
offset(r = rim_thickness)
|
||||
rounded_rect_2d(lens_width, lens_height, lens_corner_r);
|
||||
// Right lens surround
|
||||
translate([lens_cx, 0])
|
||||
offset(r = rim_thickness)
|
||||
rounded_rect_2d(lens_width, lens_height, lens_corner_r);
|
||||
// Bridge
|
||||
translate([0, lens_height/2 - nose_bridge_drop])
|
||||
square([bridge_gap + rim_thickness * 1.5, rim_thickness * 2],
|
||||
center = true);
|
||||
// Thickened brow bar — the key structural element
|
||||
translate([0, lens_height/2 + brow_thickness/2 - 0.5])
|
||||
square([face_width + 2, brow_thickness], center = true);
|
||||
}
|
||||
// Lens openings
|
||||
translate([-lens_cx, 0])
|
||||
rounded_rect_2d(lens_width, lens_height, lens_corner_r);
|
||||
translate([lens_cx, 0])
|
||||
rounded_rect_2d(lens_width, lens_height, lens_corner_r);
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// FRAME FRONT — 3D with LED channels and filter slots
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
module frame_front_3d() {
|
||||
color(CLR_FRAME)
|
||||
difference() {
|
||||
// Base frame shape
|
||||
minkowski() {
|
||||
linear_extrude(height = frame_depth - frame_fillet * 2,
|
||||
center = true)
|
||||
offset(delta = -frame_fillet)
|
||||
frame_front_2d();
|
||||
sphere(r = frame_fillet, $fn = 16);
|
||||
}
|
||||
|
||||
// ── Filter slot (front face) ──
|
||||
// A narrow horizontal groove across the brow bar front
|
||||
// The IR filter strip press-fits into this slot
|
||||
translate([0, filter_y, frame_depth/2 - filter_recess])
|
||||
cube([filter_length_total, filter_width, filter_depth * 2],
|
||||
center = true);
|
||||
|
||||
// ── Internal LED channel ──
|
||||
// Runs the full width of brow bar, behind the filter slot
|
||||
// Houses the flex PCB strip with SMD LEDs
|
||||
translate([0, filter_y, 0])
|
||||
cube([filter_length_total - 4, channel_height, channel_depth],
|
||||
center = true);
|
||||
|
||||
// ── Wire channels from brow to temples ──
|
||||
for (sx = [-1, 1])
|
||||
translate([sx * (face_width/2 - 2), filter_y, 0])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = 2.2, h = 15, center = true, $fn = 16);
|
||||
|
||||
// ── Temple LED slots (near hinges) ──
|
||||
for (sx = [-1, 1])
|
||||
for (i = [0 : led_count_temple - 1]) {
|
||||
tx = sx * (face_width/2 + 2 + i * temple_led_pitch);
|
||||
translate([tx, 0, frame_depth/2 - filter_recess])
|
||||
cube([3.8, 3.2, filter_depth * 2], center = true);
|
||||
// Cavity behind
|
||||
translate([tx, 0, 0])
|
||||
cube([4.5, 4.0, channel_depth], center = true);
|
||||
}
|
||||
|
||||
// ── Bridge LED slot ──
|
||||
if (led_count_bridge > 0)
|
||||
translate([0, lens_height/2 - nose_bridge_drop,
|
||||
frame_depth/2 - filter_recess]) {
|
||||
cube([3.8, 3.2, filter_depth * 2], center = true);
|
||||
translate([0, 0, -channel_depth/2])
|
||||
cube([4.5, 4.0, channel_depth], center = true);
|
||||
}
|
||||
|
||||
// ── Flex PCB insertion slot (side of brow bar) ──
|
||||
// The strip slides in from the temple end
|
||||
for (sx = [-1, 1])
|
||||
translate([sx * (filter_length_total/2 + 1), filter_y, 0])
|
||||
cube([4, 4.5, 1.0], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// IR-PASS FILTER STRIPS — the stealth magic
|
||||
// Looks like a subtle dark accent line on the frame
|
||||
// Actually transmits 940nm IR while blocking visible light
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
module filter_strips() {
|
||||
// Main brow bar filter — continuous strip
|
||||
translate([0, filter_y, frame_depth/2 - filter_recess/2])
|
||||
ir_filter_strip(
|
||||
length = filter_length_total - 1,
|
||||
width = filter_width - 0.2,
|
||||
thickness = filter_depth
|
||||
);
|
||||
|
||||
// Temple LED filters (small individual pieces)
|
||||
for (sx = [-1, 1])
|
||||
for (i = [0 : led_count_temple - 1]) {
|
||||
tx = sx * (face_width/2 + 2 + i * temple_led_pitch);
|
||||
translate([tx, 0, frame_depth/2 - filter_recess/2])
|
||||
ir_filter_strip(length = 3.4, width = 2.8,
|
||||
thickness = filter_depth);
|
||||
}
|
||||
|
||||
// Bridge filter
|
||||
if (led_count_bridge > 0)
|
||||
translate([0, lens_height/2 - nose_bridge_drop,
|
||||
frame_depth/2 - filter_recess/2])
|
||||
ir_filter_strip(length = 3.4, width = 2.8,
|
||||
thickness = filter_depth);
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// FLEX PCB + SMD LEDs — hidden inside the brow bar
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
module internal_led_assembly() {
|
||||
// Left brow flex strip
|
||||
translate([-(half_bridge + filter_length_side/2 + 4),
|
||||
filter_y, 0])
|
||||
rotate([0, 0, 0])
|
||||
flex_pcb_strip(
|
||||
length = filter_length_side + 4,
|
||||
led_count = led_count_brow,
|
||||
led_pitch = led_pitch
|
||||
);
|
||||
|
||||
// Right brow flex strip
|
||||
translate([(half_bridge + filter_length_side/2 + 4),
|
||||
filter_y, 0])
|
||||
rotate([0, 0, 0])
|
||||
flex_pcb_strip(
|
||||
length = filter_length_side + 4,
|
||||
led_count = led_count_brow,
|
||||
led_pitch = led_pitch
|
||||
);
|
||||
|
||||
// Bridge LED (single SMD)
|
||||
if (led_count_bridge > 0)
|
||||
translate([0, lens_height/2 - nose_bridge_drop, 0])
|
||||
ir_led_smd(show_beam = false);
|
||||
|
||||
// Temple LEDs
|
||||
for (sx = [-1, 1])
|
||||
for (i = [0 : led_count_temple - 1]) {
|
||||
tx = sx * (face_width/2 + 2 + i * temple_led_pitch);
|
||||
translate([tx, 0, 0])
|
||||
ir_led_smd(show_beam = false);
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// IR BEAM VISUALIZATION — what cameras see
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
module ir_beams() {
|
||||
beam_alpha = camera_view ? 0.12 : 0.04;
|
||||
beam_color = camera_view
|
||||
? [1.0, 1.0, 1.0, 0.15] // white bloom (camera saturated)
|
||||
: [0.55, 0.0, 1.0, beam_alpha]; // faint purple
|
||||
|
||||
// Brow bar beams — wide wash from the filter strip
|
||||
for (sx = [-1, 1])
|
||||
for (i = [0 : led_count_brow - 1]) {
|
||||
x = sx * (half_bridge + 8 + i * led_pitch);
|
||||
translate([x, filter_y, frame_depth/2])
|
||||
color(beam_color)
|
||||
// Wide angle SMD beam (±60°)
|
||||
cylinder(d1 = filter_width, d2 = 90, h = 70, $fn = 24);
|
||||
}
|
||||
|
||||
// Bridge beam
|
||||
if (led_count_bridge > 0)
|
||||
translate([0, lens_height/2 - nose_bridge_drop, frame_depth/2])
|
||||
color(beam_color)
|
||||
cylinder(d1 = 3, d2 = 60, h = 50, $fn = 24);
|
||||
|
||||
// Temple beams
|
||||
for (sx = [-1, 1])
|
||||
for (i = [0 : led_count_temple - 1]) {
|
||||
tx = sx * (face_width/2 + 2 + i * temple_led_pitch);
|
||||
translate([tx, 0, frame_depth/2])
|
||||
color(beam_color)
|
||||
cylinder(d1 = 3, d2 = 50, h = 45, $fn = 24);
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// NOSE BRIDGE
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
module nose_assembly() {
|
||||
for (sx = [-1, 1]) {
|
||||
color(CLR_SILVER)
|
||||
translate([sx * 5, -lens_height/2 + nose_bridge_drop, 0])
|
||||
rotate([20, 0, sx * 15])
|
||||
cylinder(d = 1.2, h = 14, $fn = 12);
|
||||
translate([sx * 7, -lens_height/2 + nose_bridge_drop - 10,
|
||||
frame_depth/2 + 2])
|
||||
rotate([30, sx * 10, 0])
|
||||
nose_pad();
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// TEMPLE ARM — clean with hidden wire channel
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
module temple_arm() {
|
||||
color(CLR_FRAME)
|
||||
difference() {
|
||||
union() {
|
||||
// Main section
|
||||
minkowski() {
|
||||
cube([ear_bend_start - 2,
|
||||
temple_width - 1, temple_thickness - 0.5],
|
||||
center = true);
|
||||
sphere(r = 0.5, $fn = 12);
|
||||
}
|
||||
// Ear bend
|
||||
translate([ear_bend_start/2, 0, 0])
|
||||
rotate([0, ear_bend_angle, 0])
|
||||
translate([(temple_length - ear_bend_start)/2, 0, 0])
|
||||
minkowski() {
|
||||
cube([temple_length - ear_bend_start - 2,
|
||||
temple_width - 1,
|
||||
temple_thickness - 0.5],
|
||||
center = true);
|
||||
sphere(r = 0.5, $fn = 12);
|
||||
}
|
||||
// Battery bulge (subtle thickening at tip)
|
||||
translate([ear_bend_start/2, 0, 0])
|
||||
rotate([0, ear_bend_angle, 0])
|
||||
translate([(temple_length - ear_bend_start)/2 + 5,
|
||||
0, 0])
|
||||
minkowski() {
|
||||
cube([batt_length + 2, batt_width + 1,
|
||||
batt_thickness], center = true);
|
||||
sphere(r = 1.0, $fn = 12);
|
||||
}
|
||||
}
|
||||
|
||||
// Wire channel (full length)
|
||||
rotate([0, 90, 0])
|
||||
cylinder(d = 1.8, h = ear_bend_start + 10,
|
||||
center = true, $fn = 12);
|
||||
|
||||
// Battery cavity
|
||||
translate([ear_bend_start/2, 0, 0])
|
||||
rotate([0, ear_bend_angle, 0])
|
||||
translate([(temple_length - ear_bend_start)/2 + 5,
|
||||
0, 0.5])
|
||||
cube([batt_length - 2, batt_width - 2,
|
||||
batt_thickness - 0.5], center = true);
|
||||
|
||||
// Charge port (bottom of battery area, very discreet)
|
||||
translate([ear_bend_start/2, 0, 0])
|
||||
rotate([0, ear_bend_angle, 0])
|
||||
translate([(temple_length - ear_bend_start)/2 + 5,
|
||||
0, -(batt_thickness/2 + 1)])
|
||||
cube([9, 3.2, 4], center = true);
|
||||
|
||||
// Button recess (tiny, inner face of temple)
|
||||
translate([5, -(temple_width/2 + 0.5), 0])
|
||||
cube([4, 2, 2.5], center = true);
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// HINGE
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
module hinge_block() {
|
||||
color(CLR_FRAME)
|
||||
difference() {
|
||||
minkowski() {
|
||||
cube([8, 4, frame_depth - 1], center = true);
|
||||
sphere(r = 0.5, $fn = 12);
|
||||
}
|
||||
cylinder(d = 1.5, h = frame_depth + 2,
|
||||
center = true, $fn = 16);
|
||||
}
|
||||
color(CLR_SILVER)
|
||||
cylinder(d = 1.2, h = frame_depth + 1,
|
||||
center = true, $fn = 12);
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// TEMPLE INTERNALS
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
module temple_electronics() {
|
||||
// Battery
|
||||
tip_x = ear_bend_start/2;
|
||||
translate([tip_x, 0, 0])
|
||||
rotate([0, ear_bend_angle, 0])
|
||||
translate([(temple_length - ear_bend_start)/2 + 5, 0, 1])
|
||||
rotate([0, 0, 90])
|
||||
lipo_battery(l = batt_length - 4,
|
||||
w = batt_width - 4,
|
||||
h = batt_thickness - 1.5);
|
||||
|
||||
// Tiny MCU (one temple only)
|
||||
translate([15, 0, 0.5])
|
||||
scale(0.5)
|
||||
attiny85_dip8();
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// FULL ASSEMBLY
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
module full_assembly() {
|
||||
|
||||
// ── Frame front ──
|
||||
translate([0, 0, ex * 0.3])
|
||||
frame_front_3d();
|
||||
|
||||
// ── IR-pass filter strips ──
|
||||
if (show_filter)
|
||||
translate([0, 0, ex * 0.5])
|
||||
filter_strips();
|
||||
|
||||
// ── Internal flex PCB + SMD LEDs ──
|
||||
if (show_flex_pcb)
|
||||
translate([0, 0, ex * 0.15])
|
||||
internal_led_assembly();
|
||||
|
||||
// ── IR beams ──
|
||||
if (show_beams)
|
||||
translate([0, 0, ex * 0.5])
|
||||
ir_beams();
|
||||
|
||||
// ── Nose pads ──
|
||||
nose_assembly();
|
||||
|
||||
// ── Hinges + Temples ──
|
||||
for (side = [-1, 1]) {
|
||||
hinge_x = side * (face_width / 2 + 2);
|
||||
|
||||
translate([hinge_x, 0, 0])
|
||||
hinge_block();
|
||||
|
||||
translate([hinge_x + side * ear_bend_start / 2,
|
||||
0, -ex * 0.2])
|
||||
rotate([0, 0, side * temple_angle])
|
||||
translate([side * 5, 0, 0]) {
|
||||
temple_arm();
|
||||
if (show_internals)
|
||||
temple_electronics();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// PRINT LAYOUT
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
module print_parts() {
|
||||
// Frame front (flat on back)
|
||||
translate([0, 0, frame_depth/2])
|
||||
rotate([90, 0, 0])
|
||||
frame_front_3d();
|
||||
// Left temple
|
||||
translate([0, -55, temple_thickness/2])
|
||||
temple_arm();
|
||||
// Right temple
|
||||
translate([0, -75, temple_thickness/2])
|
||||
temple_arm();
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// RENDER
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
if (print_layout) {
|
||||
print_parts();
|
||||
} else if (cross_section) {
|
||||
difference() {
|
||||
full_assembly();
|
||||
// Cut front half to expose LED channel
|
||||
translate([0, 0, 50 + frame_depth/2 - 1])
|
||||
cube([300, 300, 100], center = true);
|
||||
}
|
||||
} else {
|
||||
full_assembly();
|
||||
}
|
||||
|
||||
// ── Info overlay ──
|
||||
color("White")
|
||||
translate([0, -lens_height - 15, 0])
|
||||
linear_extrude(0.1)
|
||||
text(str("ZK-Glasses Stealth — ", total_leds,
|
||||
" SMD IR LEDs @ 940nm"),
|
||||
size = 3.5, halign = "center",
|
||||
font = "Liberation Mono");
|
||||
|
||||
color("Gray")
|
||||
translate([0, -lens_height - 22, 0])
|
||||
linear_extrude(0.1)
|
||||
text(str("Frame: ", face_width,
|
||||
"mm | IR filter: Wratten 87C equiv"),
|
||||
size = 2.8, halign = "center",
|
||||
font = "Liberation Mono");
|
||||
|
||||
color([0.55, 0, 1], 0.4)
|
||||
translate([0, -lens_height - 28, 0])
|
||||
linear_extrude(0.1)
|
||||
text("Invisible to eyes — visible only to cameras",
|
||||
size = 2.5, halign = "center",
|
||||
font = "Liberation Mono:style=Italic");
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// NOTES
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
//
|
||||
// STEALTH DESIGN:
|
||||
// The brow bar has a thin horizontal slot cut into its
|
||||
// front face. An IR-pass filter strip (Kodak Wratten 87C
|
||||
// or Hoya IR-85) press-fits into this slot. It looks like
|
||||
// a subtle dark decorative accent line — completely normal.
|
||||
//
|
||||
// Behind the filter: a flex PCB strip with SMD IR LEDs
|
||||
// (OSRAM SFH 4726AS or similar, 940nm, PLCC-2 package).
|
||||
// Each LED is 3.5 × 2.8 × 1.9mm — invisible from outside.
|
||||
//
|
||||
// The 940nm wavelength is completely invisible to human
|
||||
// eyes (no red glow), but cameras see bright white spots.
|
||||
//
|
||||
// IR-PASS FILTER OPTIONS:
|
||||
// - Kodak Wratten 87C gel: cheapest, ~$5, can be cut to size
|
||||
// - Hoya IR-85 glass: durable, optical quality, ~$20
|
||||
// - Lee 87C polyester: thin, flexible, press-fits easily
|
||||
// - 3D print in dark PLA, thin enough to pass some IR
|
||||
//
|
||||
// ASSEMBLY:
|
||||
// 1. 3D print frame (PETG, matte black)
|
||||
// 2. Solder SMD LEDs to flex PCB strip
|
||||
// 3. Slide flex strip into brow bar channel
|
||||
// 4. Route wires through temple channels
|
||||
// 5. Press-fit IR filter strips into front slots
|
||||
// 6. Insert batteries into temple tips
|
||||
// 7. Snap in charge port, button, MCU
|
||||
//
|
||||
// PRINT SETTINGS:
|
||||
// Material: PETG matte black (or Nylon PA12)
|
||||
// Layer: 0.12mm (fine detail for filter slot)
|
||||
// Infill: 40% gyroid
|
||||
// Walls: 4 perimeters (structural around channels)
|
||||
// Supports: Tree, touching buildplate only
|
||||
// Post: Light sanding, optional soft-touch paint
|
||||
//
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 212 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
|
@ -0,0 +1,612 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1400 900" width="1400" height="900">
|
||||
<defs>
|
||||
<style>
|
||||
text { font-family: 'Courier New', monospace; }
|
||||
.title { font-size: 18px; fill: #c5c8c6; font-weight: bold; }
|
||||
.subtitle { font-size: 12px; fill: #969896; }
|
||||
.ref { font-size: 11px; fill: #b294bb; font-weight: bold; }
|
||||
.val { font-size: 10px; fill: #81a2be; }
|
||||
.net { font-size: 10px; fill: #b5bd68; font-weight: bold; }
|
||||
.pin { font-size: 9px; fill: #969896; }
|
||||
.wire { stroke: #c5c8c6; stroke-width: 1.5; fill: none; stroke-linecap: round; stroke-linejoin: round; }
|
||||
.wire-pwr { stroke: #cc6666; stroke-width: 2; fill: none; stroke-linecap: round; }
|
||||
.wire-gnd { stroke: #969896; stroke-width: 1.5; fill: none; stroke-linecap: round; }
|
||||
.comp { stroke: #c5c8c6; stroke-width: 1.5; fill: none; }
|
||||
.comp-fill { stroke: #c5c8c6; stroke-width: 1.5; fill: #282a2e; }
|
||||
.ic-fill { stroke: #b294bb; stroke-width: 2; fill: #1d1f21; }
|
||||
.led-fill { stroke: #cc6666; stroke-width: 1.5; fill: rgba(204,102,102,0.15); }
|
||||
.led-ir { stroke: #b294bb; stroke-width: 1.5; fill: rgba(178,148,187,0.15); }
|
||||
.mosfet-fill { stroke: #81a2be; stroke-width: 1.5; fill: #1d1f21; }
|
||||
.cap-fill { stroke: #81a2be; stroke-width: 1.5; fill: none; }
|
||||
.batt-fill { stroke: #de935f; stroke-width: 2; fill: none; }
|
||||
.dot { fill: #c5c8c6; }
|
||||
.gnd-sym { stroke: #969896; stroke-width: 1.5; fill: none; }
|
||||
.vcc-sym { stroke: #cc6666; stroke-width: 1.5; fill: none; }
|
||||
.frame { stroke: #373b41; stroke-width: 1; fill: none; }
|
||||
.section { stroke: #373b41; stroke-width: 0.5; fill: none; stroke-dasharray: 4,4; }
|
||||
.section-label { font-size: 11px; fill: #5e6266; font-style: italic; }
|
||||
.beam { fill: url(#irGlow); }
|
||||
</style>
|
||||
<linearGradient id="irGlow" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#b294bb;stop-opacity:0.3" />
|
||||
<stop offset="100%" style="stop-color:#b294bb;stop-opacity:0" />
|
||||
</linearGradient>
|
||||
<marker id="arrowhead" markerWidth="6" markerHeight="4" refX="6" refY="2" orient="auto">
|
||||
<polygon points="0 0, 6 2, 0 4" fill="#b5bd68"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="1400" height="900" fill="#1d1f21"/>
|
||||
|
||||
<!-- Border frame -->
|
||||
<rect x="10" y="10" width="1380" height="880" class="frame"/>
|
||||
<rect x="15" y="15" width="1370" height="870" class="frame"/>
|
||||
|
||||
<!-- Title block -->
|
||||
<rect x="15" y="830" width="1370" height="55" class="frame"/>
|
||||
<text x="30" y="855" class="title">ZK-GLASSES / IR Anti-Surveillance Eyewear / Driver Circuit</text>
|
||||
<text x="30" y="875" class="subtitle">ATtiny85 PWM Controller | 16x 940nm IR LEDs | LiPo 3.7V 500mAh | Rev 1.0 | 2026-02-21</text>
|
||||
<text x="1200" y="855" class="subtitle">Sheet 1/1</text>
|
||||
<text x="1200" y="875" class="subtitle">zk-glasses.svg</text>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════ -->
|
||||
<!-- SECTION: Power Supply (left side) -->
|
||||
<!-- ═══════════════════════════════════════════════════════ -->
|
||||
<rect x="25" y="30" width="350" height="380" class="section" rx="8"/>
|
||||
<text x="35" y="48" class="section-label">POWER SUPPLY</text>
|
||||
|
||||
<!-- Battery BT1 -->
|
||||
<line x1="80" y1="120" x2="80" y2="145" class="batt-fill"/>
|
||||
<line x1="65" y1="145" x2="95" y2="145" class="batt-fill" stroke-width="3"/>
|
||||
<line x1="72" y1="155" x2="88" y2="155" class="batt-fill" stroke-width="1.5"/>
|
||||
<line x1="80" y1="155" x2="80" y2="180" class="batt-fill"/>
|
||||
<text x="55" y="118" class="ref">BT1</text>
|
||||
<text x="95" y="150" class="val">LiPo</text>
|
||||
<text x="95" y="162" class="val">3.7V</text>
|
||||
<text x="95" y="174" class="val">500mAh</text>
|
||||
<text x="68" y="138" class="pin">+</text>
|
||||
<text x="72" y="170" class="pin">-</text>
|
||||
|
||||
<!-- GND symbol under battery -->
|
||||
<line x1="80" y1="180" x2="80" y2="195" class="wire-gnd"/>
|
||||
<line x1="68" y1="195" x2="92" y2="195" class="gnd-sym"/>
|
||||
<line x1="72" y1="200" x2="88" y2="200" class="gnd-sym"/>
|
||||
<line x1="76" y1="205" x2="84" y2="205" class="gnd-sym"/>
|
||||
|
||||
<!-- Wire from BT1+ up to switch -->
|
||||
<line x1="80" y1="120" x2="80" y2="80" class="wire-pwr"/>
|
||||
|
||||
<!-- Slide Switch SW2 -->
|
||||
<line x1="80" y1="80" x2="80" y2="70" class="wire-pwr"/>
|
||||
<circle cx="80" cy="70" r="3" class="comp" fill="#1d1f21"/>
|
||||
<line x1="80" y1="67" x2="120" y2="55" class="wire-pwr" stroke-width="2"/>
|
||||
<circle cx="120" cy="55" r="3" class="comp" fill="#1d1f21"/>
|
||||
<line x1="120" y1="55" x2="120" y2="50" class="wire-pwr"/>
|
||||
<text x="55" y="65" class="ref">SW2</text>
|
||||
<text x="55" y="78" class="val">SPDT</text>
|
||||
<text x="85" y="90" class="pin">Power</text>
|
||||
|
||||
<!-- VCC rail from switch -->
|
||||
<line x1="120" y1="50" x2="120" y2="40" class="wire-pwr"/>
|
||||
<!-- VCC symbol -->
|
||||
<line x1="115" y1="40" x2="125" y2="40" class="vcc-sym"/>
|
||||
<line x1="120" y1="40" x2="120" y2="33" class="vcc-sym"/>
|
||||
<text x="128" y="38" class="net">VCC 3.7V</text>
|
||||
|
||||
<!-- TP4056 Charge Board -->
|
||||
<rect x="170" y="80" width="130" height="70" class="ic-fill" rx="4"/>
|
||||
<text x="200" y="105" class="ref">TP4056</text>
|
||||
<text x="193" y="120" class="val">USB-C Charger</text>
|
||||
<text x="197" y="135" class="val">w/ Protection</text>
|
||||
|
||||
<!-- TP4056 pins -->
|
||||
<text x="148" y="95" class="pin">USB+</text>
|
||||
<line x1="170" y1="92" x2="155" y2="92" class="wire"/>
|
||||
<text x="148" y="140" class="pin">USB-</text>
|
||||
<line x1="170" y1="137" x2="155" y2="137" class="wire"/>
|
||||
|
||||
<!-- USB-C icon -->
|
||||
<rect x="130" y="85" width="22" height="14" rx="4" class="comp-fill"/>
|
||||
<text x="133" y="96" style="font-size:8px;fill:#969896">USB</text>
|
||||
<line x1="152" y1="92" x2="155" y2="92" class="wire"/>
|
||||
|
||||
<!-- TP4056 BAT+ output -->
|
||||
<line x1="300" y1="92" x2="340" y2="92" class="wire-pwr"/>
|
||||
<text x="302" y="88" class="pin">BAT+</text>
|
||||
<line x1="340" y1="92" x2="340" y2="50" class="wire-pwr"/>
|
||||
<line x1="340" y1="50" x2="120" y2="50" class="wire-pwr"/>
|
||||
<circle cx="120" cy="50" r="2.5" class="dot"/>
|
||||
|
||||
<!-- TP4056 BAT- output -->
|
||||
<line x1="300" y1="137" x2="340" y2="137" class="wire-gnd"/>
|
||||
<text x="302" y="133" class="pin">BAT-</text>
|
||||
<line x1="340" y1="137" x2="340" y2="195" class="wire-gnd"/>
|
||||
<line x1="328" y1="195" x2="352" y2="195" class="gnd-sym"/>
|
||||
<line x1="332" y1="200" x2="348" y2="200" class="gnd-sym"/>
|
||||
<line x1="336" y1="205" x2="344" y2="205" class="gnd-sym"/>
|
||||
|
||||
<!-- Charging LED indicators (small) -->
|
||||
<circle cx="185" y="160" r="3" fill="rgba(204,102,102,0.4)" stroke="#cc6666" stroke-width="1"/>
|
||||
<text x="192" y="163" style="font-size:8px;fill:#cc6666">CHG</text>
|
||||
<circle cx="245" y="160" r="3" fill="rgba(181,189,104,0.4)" stroke="#b5bd68" stroke-width="1"/>
|
||||
<text x="252" y="163" style="font-size:8px;fill:#b5bd68">DONE</text>
|
||||
|
||||
<!-- Decoupling cap C1 -->
|
||||
<line x1="250" y1="50" x2="250" y2="230" class="wire"/>
|
||||
<circle cx="250" cy="50" r="2.5" class="dot"/>
|
||||
<line x1="240" y1="228" x2="260" y2="228" class="cap-fill" stroke-width="2"/>
|
||||
<line x1="240" y1="235" x2="260" y2="235" class="cap-fill" stroke-width="2"/>
|
||||
<line x1="250" y1="235" x2="250" y2="255" class="wire-gnd"/>
|
||||
<line x1="238" y1="255" x2="262" y2="255" class="gnd-sym"/>
|
||||
<line x1="242" y1="260" x2="258" y2="260" class="gnd-sym"/>
|
||||
<line x1="246" y1="265" x2="254" y2="265" class="gnd-sym"/>
|
||||
<text x="263" y="225" class="ref">C1</text>
|
||||
<text x="263" y="238" class="val">100nF</text>
|
||||
|
||||
<!-- Voltage Divider for battery sense -->
|
||||
<text x="35" y="245" class="section-label">BATT SENSE</text>
|
||||
|
||||
<!-- R20 top -->
|
||||
<line x1="80" y1="250" x2="80" y2="265" class="wire"/>
|
||||
<!-- Resistor body R20 -->
|
||||
<rect x="72" y="265" width="16" height="40" class="comp-fill" rx="2"/>
|
||||
<text x="92" y="282" class="ref">R20</text>
|
||||
<text x="92" y="294" class="val">100k</text>
|
||||
<!-- Junction between R20 and R21 -->
|
||||
<line x1="80" y1="305" x2="80" y2="315" class="wire"/>
|
||||
<circle cx="80" cy="315" r="2.5" class="dot"/>
|
||||
<!-- Line out to VBAT label -->
|
||||
<line x1="80" y1="315" x2="130" y2="315" class="wire"/>
|
||||
<text x="133" y="319" class="net">VBAT_SENSE</text>
|
||||
<!-- R21 bottom -->
|
||||
<line x1="80" y1="315" x2="80" y2="325" class="wire"/>
|
||||
<rect x="72" y="325" width="16" height="40" class="comp-fill" rx="2"/>
|
||||
<text x="92" y="342" class="ref">R21</text>
|
||||
<text x="92" y="354" class="val">100k</text>
|
||||
<line x1="80" y1="365" x2="80" y2="385" class="wire-gnd"/>
|
||||
<line x1="68" y1="385" x2="92" y2="385" class="gnd-sym"/>
|
||||
<line x1="72" y1="390" x2="88" y2="390" class="gnd-sym"/>
|
||||
<line x1="76" y1="395" x2="84" y2="395" class="gnd-sym"/>
|
||||
|
||||
<!-- Connect R20 top to VCC -->
|
||||
<line x1="80" y1="250" x2="80" y2="240" class="wire-pwr"/>
|
||||
<line x1="80" y1="240" x2="120" y2="240" class="wire-pwr"/>
|
||||
<line x1="120" y1="240" x2="120" y2="50" class="wire-pwr"/>
|
||||
<circle cx="120" cy="240" r="2.5" class="dot"/>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════ -->
|
||||
<!-- SECTION: Microcontroller (center) -->
|
||||
<!-- ═══════════════════════════════════════════════════════ -->
|
||||
<rect x="400" y="30" width="320" height="470" class="section" rx="8"/>
|
||||
<text x="410" y="48" class="section-label">MICROCONTROLLER</text>
|
||||
|
||||
<!-- ATtiny85 IC body -->
|
||||
<rect x="470" y="120" width="160" height="200" class="ic-fill" rx="6"/>
|
||||
<!-- IC notch -->
|
||||
<path d="M 545 120 A 10 10 0 0 1 555 120" class="ic-fill" fill="none"/>
|
||||
|
||||
<text x="510" y="175" class="title" style="font-size:16px;fill:#b294bb">ATtiny85</text>
|
||||
<text x="518" y="195" class="val" style="font-size:11px">DIP-8</text>
|
||||
<text x="520" y="210" class="val" style="font-size:9px">8MHz int.</text>
|
||||
|
||||
<!-- Pin labels (left side: 1-4) -->
|
||||
<!-- Pin 1: PB5/RESET -->
|
||||
<line x1="470" y1="155" x2="430" y2="155" class="wire"/>
|
||||
<text x="475" y="159" class="pin" style="text-anchor:start">PB5</text>
|
||||
<text x="393" y="159" class="pin">(RST) 1</text>
|
||||
|
||||
<!-- Pin 2: PB3 (status LED) -->
|
||||
<line x1="470" y1="195" x2="430" y2="195" class="wire"/>
|
||||
<text x="475" y="199" class="pin" style="text-anchor:start">PB3</text>
|
||||
<text x="410" y="199" class="pin">2</text>
|
||||
|
||||
<!-- Pin 3: PB4 (ADC - battery sense) -->
|
||||
<line x1="470" y1="235" x2="430" y2="235" class="wire"/>
|
||||
<text x="475" y="239" class="pin" style="text-anchor:start">PB4</text>
|
||||
<text x="410" y="239" class="pin">3</text>
|
||||
|
||||
<!-- Pin 4: GND -->
|
||||
<line x1="470" y1="275" x2="430" y2="275" class="wire-gnd"/>
|
||||
<text x="475" y="279" class="pin" style="text-anchor:start">GND</text>
|
||||
<text x="410" y="279" class="pin">4</text>
|
||||
|
||||
<!-- Pin labels (right side: 5-8) -->
|
||||
<!-- Pin 5: PB0 (LED_L) -->
|
||||
<line x1="630" y1="275" x2="680" y2="275" class="wire"/>
|
||||
<text x="620" y="279" class="pin" style="text-anchor:end">PB0</text>
|
||||
<text x="685" y="279" class="pin">5</text>
|
||||
|
||||
<!-- Pin 6: PB1 (LED_R) -->
|
||||
<line x1="630" y1="235" x2="680" y2="235" class="wire"/>
|
||||
<text x="620" y="239" class="pin" style="text-anchor:end">PB1</text>
|
||||
<text x="685" y="239" class="pin">6</text>
|
||||
|
||||
<!-- Pin 7: PB2 (Button) -->
|
||||
<line x1="630" y1="195" x2="680" y2="195" class="wire"/>
|
||||
<text x="620" y="199" class="pin" style="text-anchor:end">PB2</text>
|
||||
<text x="685" y="199" class="pin">7</text>
|
||||
|
||||
<!-- Pin 8: VCC -->
|
||||
<line x1="630" y1="155" x2="680" y2="155" class="wire-pwr"/>
|
||||
<text x="620" y="159" class="pin" style="text-anchor:end">VCC</text>
|
||||
<text x="685" y="159" class="pin">8</text>
|
||||
|
||||
<!-- VCC connection to pin 8 -->
|
||||
<line x1="680" y1="155" x2="700" y2="155" class="wire-pwr"/>
|
||||
<line x1="700" y1="155" x2="700" y2="80" class="wire-pwr"/>
|
||||
<line x1="700" y1="80" x2="340" y2="80" class="wire-pwr"/>
|
||||
<line x1="340" y1="80" x2="340" y2="50" class="wire-pwr"/>
|
||||
<circle cx="340" cy="80" r="2.5" class="dot"/>
|
||||
<text x="705" y="85" class="net">VCC</text>
|
||||
|
||||
<!-- GND connection from pin 4 -->
|
||||
<line x1="430" y1="275" x2="430" y2="310" class="wire-gnd"/>
|
||||
<line x1="418" y1="310" x2="442" y2="310" class="gnd-sym"/>
|
||||
<line x1="422" y1="315" x2="438" y2="315" class="gnd-sym"/>
|
||||
<line x1="426" y1="320" x2="434" y2="320" class="gnd-sym"/>
|
||||
|
||||
<!-- RST pullup to VCC -->
|
||||
<line x1="430" y1="155" x2="430" y2="100" class="wire"/>
|
||||
<rect x="422" y="100" width="16" height="30" class="comp-fill" rx="2"/>
|
||||
<line x1="430" y1="100" x2="430" y2="80" class="wire-pwr"/>
|
||||
<line x1="430" y1="80" x2="340" y2="80" class="wire-pwr"/>
|
||||
<circle cx="430" cy="80" r="2.5" class="dot"/>
|
||||
<text x="445" y="112" class="ref">R22</text>
|
||||
<text x="445" y="124" class="val">10k</text>
|
||||
|
||||
<!-- PB3 → Status LED path -->
|
||||
<line x1="430" y1="195" x2="430" y2="380" class="wire"/>
|
||||
<!-- R19 resistor -->
|
||||
<rect x="422" y="380" width="16" height="35" class="comp-fill" rx="2"/>
|
||||
<text x="445" y="395" class="ref">R19</text>
|
||||
<text x="445" y="407" class="val">330R</text>
|
||||
<line x1="430" y1="415" x2="430" y2="430" class="wire"/>
|
||||
<!-- Status LED -->
|
||||
<polygon points="420,430 440,430 430,450" class="led-fill" fill="rgba(181,189,104,0.3)"/>
|
||||
<line x1="420" y1="450" x2="440" y2="450" class="led-fill"/>
|
||||
<!-- LED arrows -->
|
||||
<line x1="442" y1="435" x2="450" y2="428" class="wire" stroke="#b5bd68" stroke-width="1"/>
|
||||
<polygon points="450,428 446,432 449,432" fill="#b5bd68"/>
|
||||
<line x1="445" y1="440" x2="453" y2="433" class="wire" stroke="#b5bd68" stroke-width="1"/>
|
||||
<polygon points="453,433 449,437 452,437" fill="#b5bd68"/>
|
||||
<text x="455" y="443" class="ref">LED1</text>
|
||||
<text x="455" y="455" class="val">Green</text>
|
||||
<line x1="430" y1="450" x2="430" y2="470" class="wire-gnd"/>
|
||||
<line x1="418" y1="470" x2="442" y2="470" class="gnd-sym"/>
|
||||
<line x1="422" y1="475" x2="438" y2="475" class="gnd-sym"/>
|
||||
<line x1="426" y1="480" x2="434" y2="480" class="gnd-sym"/>
|
||||
|
||||
<!-- PB4 → VBAT_SENSE -->
|
||||
<line x1="430" y1="235" x2="380" y2="235" class="wire"/>
|
||||
<line x1="380" y1="235" x2="380" y2="315" class="wire"/>
|
||||
<line x1="380" y1="315" x2="130" y2="315" class="wire"/>
|
||||
<circle cx="130" cy="315" r="0" class="dot"/>
|
||||
<text x="383" y="290" class="net" style="font-size:9px" transform="rotate(-90,383,290)">VBAT_SENSE</text>
|
||||
|
||||
<!-- PB2 → Button -->
|
||||
<line x1="680" y1="195" x2="700" y2="195" class="wire"/>
|
||||
<line x1="700" y1="195" x2="700" y2="360" class="wire"/>
|
||||
<!-- Button symbol -->
|
||||
<circle cx="700" cy="360" r="3" class="comp" fill="#1d1f21"/>
|
||||
<line x1="700" y1="363" x2="700" y2="375" class="wire"/>
|
||||
<line x1="690" y1="375" x2="710" y2="375" class="comp"/>
|
||||
<line x1="688" y1="385" x2="712" y2="385" class="comp"/>
|
||||
<line x1="700" y1="385" x2="700" y2="400" class="wire"/>
|
||||
<!-- Push button bridge -->
|
||||
<line x1="693" y1="370" x2="715" y2="362" class="comp" stroke-width="2"/>
|
||||
<circle cx="700" cy="400" r="0" class="dot"/>
|
||||
<line x1="700" y1="400" x2="700" y2="415" class="wire-gnd"/>
|
||||
<line x1="688" y1="415" x2="712" y2="415" class="gnd-sym"/>
|
||||
<line x1="692" y1="420" x2="708" y2="420" class="gnd-sym"/>
|
||||
<line x1="696" y1="425" x2="704" y2="425" class="gnd-sym"/>
|
||||
<text x="715" y="370" class="ref">SW1</text>
|
||||
<text x="715" y="383" class="val">Tact</text>
|
||||
<text x="715" y="396" class="val">6x6mm</text>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════ -->
|
||||
<!-- SECTION: LED Drivers (right side) -->
|
||||
<!-- ═══════════════════════════════════════════════════════ -->
|
||||
<rect x="740" y="30" width="640" height="790" class="section" rx="8"/>
|
||||
<text x="750" y="48" class="section-label">IR LED DRIVER — 16x 940nm</text>
|
||||
|
||||
<!-- ──── LEFT LED GROUP (PB0 → Q1 → 8 LEDs) ──── -->
|
||||
|
||||
<!-- Wire from PB0 to gate resistor R17 -->
|
||||
<line x1="680" y1="275" x2="780" y2="275" class="wire"/>
|
||||
<text x="750" y="270" class="net">LED_L</text>
|
||||
|
||||
<!-- R17 gate resistor -->
|
||||
<rect x="780" y="267" width="35" height="16" class="comp-fill" rx="2"/>
|
||||
<text x="787" y="262" class="ref">R17</text>
|
||||
<text x="788" y="292" class="val">1k</text>
|
||||
|
||||
<!-- Wire to MOSFET Q1 gate -->
|
||||
<line x1="815" y1="275" x2="845" y2="275" class="wire"/>
|
||||
|
||||
<!-- MOSFET Q1 symbol -->
|
||||
<rect x="845" y="230" width="70" height="90" class="mosfet-fill" rx="4"/>
|
||||
<text x="860" y="255" class="ref">Q1</text>
|
||||
<text x="852" y="270" class="val">IRLML6344</text>
|
||||
<text x="855" y="285" class="val">N-MOSFET</text>
|
||||
<text x="855" y="300" class="val">SOT-23</text>
|
||||
<!-- Gate pin -->
|
||||
<text x="830" y="279" class="pin">G</text>
|
||||
<!-- Drain pin (top) -->
|
||||
<line x1="880" y1="230" x2="880" y2="200" class="wire"/>
|
||||
<text x="885" y="225" class="pin">D</text>
|
||||
<!-- Source pin (bottom) -->
|
||||
<line x1="880" y1="320" x2="880" y2="350" class="wire-gnd"/>
|
||||
<text x="885" y="340" class="pin">S</text>
|
||||
<line x1="868" y1="350" x2="892" y2="350" class="gnd-sym"/>
|
||||
<line x1="872" y1="355" x2="888" y2="355" class="gnd-sym"/>
|
||||
<line x1="876" y1="360" x2="884" y2="360" class="gnd-sym"/>
|
||||
|
||||
<!-- VCC rail for LEDs -->
|
||||
<line x1="880" y1="80" x2="1350" y2="80" class="wire-pwr" stroke-width="2.5"/>
|
||||
<line x1="700" y1="80" x2="880" y2="80" class="wire-pwr"/>
|
||||
<circle cx="880" cy="80" r="2.5" class="dot"/>
|
||||
<text x="1100" y="72" class="net" style="font-size:12px">VCC RAIL (3.7V)</text>
|
||||
|
||||
<!-- LEFT LED array (8 LEDs with individual resistors) -->
|
||||
<!-- LED row at y=100-200, spread across x=790-1060 -->
|
||||
<g>
|
||||
<!-- 8 parallel LED+R branches from VCC to Q1 drain -->
|
||||
<!-- Branch pattern: VCC → R → LED → Q1_Drain -->
|
||||
|
||||
<!-- LED L1 -->
|
||||
<line x1="800" y1="80" x2="800" y2="100" class="wire"/>
|
||||
<rect x="794" y="100" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="800" y1="125" x2="800" y2="135" class="wire"/>
|
||||
<polygon points="793,135 807,135 800,152" class="led-ir"/>
|
||||
<line x1="793" y1="152" x2="807" y2="152" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="800" y1="152" x2="800" y2="165" class="wire"/>
|
||||
<text x="790" y="97" class="pin" style="font-size:7px">R1</text>
|
||||
|
||||
<!-- LED L2 -->
|
||||
<line x1="835" y1="80" x2="835" y2="100" class="wire"/>
|
||||
<rect x="829" y="100" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="835" y1="125" x2="835" y2="135" class="wire"/>
|
||||
<polygon points="828,135 842,135 835,152" class="led-ir"/>
|
||||
<line x1="828" y1="152" x2="842" y2="152" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="835" y1="152" x2="835" y2="165" class="wire"/>
|
||||
<text x="825" y="97" class="pin" style="font-size:7px">R2</text>
|
||||
|
||||
<!-- LED L3 -->
|
||||
<line x1="870" y1="80" x2="870" y2="100" class="wire"/>
|
||||
<rect x="864" y="100" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="870" y1="125" x2="870" y2="135" class="wire"/>
|
||||
<polygon points="863,135 877,135 870,152" class="led-ir"/>
|
||||
<line x1="863" y1="152" x2="877" y2="152" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="870" y1="152" x2="870" y2="165" class="wire"/>
|
||||
<text x="860" y="97" class="pin" style="font-size:7px">R3</text>
|
||||
|
||||
<!-- LED L4 -->
|
||||
<line x1="905" y1="80" x2="905" y2="100" class="wire"/>
|
||||
<rect x="899" y="100" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="905" y1="125" x2="905" y2="135" class="wire"/>
|
||||
<polygon points="898,135 912,135 905,152" class="led-ir"/>
|
||||
<line x1="898" y1="152" x2="912" y2="152" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="905" y1="152" x2="905" y2="165" class="wire"/>
|
||||
<text x="895" y="97" class="pin" style="font-size:7px">R4</text>
|
||||
|
||||
<!-- LED L5 -->
|
||||
<line x1="940" y1="80" x2="940" y2="100" class="wire"/>
|
||||
<rect x="934" y="100" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="940" y1="125" x2="940" y2="135" class="wire"/>
|
||||
<polygon points="933,135 947,135 940,152" class="led-ir"/>
|
||||
<line x1="933" y1="152" x2="947" y2="152" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="940" y1="152" x2="940" y2="165" class="wire"/>
|
||||
<text x="930" y="97" class="pin" style="font-size:7px">R5</text>
|
||||
|
||||
<!-- LED L6 -->
|
||||
<line x1="975" y1="80" x2="975" y2="100" class="wire"/>
|
||||
<rect x="969" y="100" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="975" y1="125" x2="975" y2="135" class="wire"/>
|
||||
<polygon points="968,135 982,135 975,152" class="led-ir"/>
|
||||
<line x1="968" y1="152" x2="982" y2="152" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="975" y1="152" x2="975" y2="165" class="wire"/>
|
||||
<text x="965" y="97" class="pin" style="font-size:7px">R6</text>
|
||||
|
||||
<!-- LED L7 -->
|
||||
<line x1="1010" y1="80" x2="1010" y2="100" class="wire"/>
|
||||
<rect x="1004" y="100" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="1010" y1="125" x2="1010" y2="135" class="wire"/>
|
||||
<polygon points="1003,135 1017,135 1010,152" class="led-ir"/>
|
||||
<line x1="1003" y1="152" x2="1017" y2="152" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="1010" y1="152" x2="1010" y2="165" class="wire"/>
|
||||
<text x="1000" y="97" class="pin" style="font-size:7px">R7</text>
|
||||
|
||||
<!-- LED L8 -->
|
||||
<line x1="1045" y1="80" x2="1045" y2="100" class="wire"/>
|
||||
<rect x="1039" y="100" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="1045" y1="125" x2="1045" y2="135" class="wire"/>
|
||||
<polygon points="1038,135 1052,135 1045,152" class="led-ir"/>
|
||||
<line x1="1038" y1="152" x2="1052" y2="152" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="1045" y1="152" x2="1045" y2="165" class="wire"/>
|
||||
<text x="1035" y="97" class="pin" style="font-size:7px">R8</text>
|
||||
|
||||
<!-- Common drain bus connecting all LED cathodes to Q1 drain -->
|
||||
<line x1="800" y1="165" x2="1045" y2="165" class="wire"/>
|
||||
<line x1="880" y1="165" x2="880" y2="200" class="wire"/>
|
||||
<circle cx="880" cy="165" r="2.5" class="dot"/>
|
||||
|
||||
<!-- IR emission glow effects (decorative) -->
|
||||
<ellipse cx="920" cy="100" rx="160" ry="25" class="beam"/>
|
||||
|
||||
<!-- Label -->
|
||||
<text x="800" y="185" class="ref" style="font-size:10px">D1–D8: TSAL6200 (940nm, 8× parallel)</text>
|
||||
<text x="800" y="197" class="val">R1–R8: 47R (each: If = (3.7-1.3)/47 = 51mA)</text>
|
||||
</g>
|
||||
|
||||
<!-- ──── RIGHT LED GROUP (PB1 → Q2 → 8 LEDs) ──── -->
|
||||
|
||||
<!-- Wire from PB1 to gate resistor R18 -->
|
||||
<line x1="680" y1="235" x2="720" y2="235" class="wire"/>
|
||||
<line x1="720" y1="235" x2="720" y2="510" class="wire"/>
|
||||
<line x1="720" y1="510" x2="780" y2="510" class="wire"/>
|
||||
<text x="750" y="505" class="net">LED_R</text>
|
||||
|
||||
<!-- R18 gate resistor -->
|
||||
<rect x="780" y="502" width="35" height="16" class="comp-fill" rx="2"/>
|
||||
<text x="787" y="497" class="ref">R18</text>
|
||||
<text x="788" y="527" class="val">1k</text>
|
||||
|
||||
<!-- Wire to MOSFET Q2 gate -->
|
||||
<line x1="815" y1="510" x2="845" y2="510" class="wire"/>
|
||||
|
||||
<!-- MOSFET Q2 symbol -->
|
||||
<rect x="845" y="465" width="70" height="90" class="mosfet-fill" rx="4"/>
|
||||
<text x="860" y="490" class="ref">Q2</text>
|
||||
<text x="852" y="505" class="val">IRLML6344</text>
|
||||
<text x="855" y="520" class="val">N-MOSFET</text>
|
||||
<text x="855" y="535" class="val">SOT-23</text>
|
||||
<text x="830" y="514" class="pin">G</text>
|
||||
<line x1="880" y1="465" x2="880" y2="435" class="wire"/>
|
||||
<text x="885" y="460" class="pin">D</text>
|
||||
<line x1="880" y1="555" x2="880" y2="585" class="wire-gnd"/>
|
||||
<text x="885" y="575" class="pin">S</text>
|
||||
<line x1="868" y1="585" x2="892" y2="585" class="gnd-sym"/>
|
||||
<line x1="872" y1="590" x2="888" y2="590" class="gnd-sym"/>
|
||||
<line x1="876" y1="595" x2="884" y2="595" class="gnd-sym"/>
|
||||
|
||||
<!-- RIGHT LED array -->
|
||||
<g>
|
||||
<!-- VCC taps for right group -->
|
||||
<line x1="800" y1="80" x2="800" y2="80" class="wire"/>
|
||||
|
||||
<!-- LED R1 -->
|
||||
<line x1="800" y1="335" x2="800" y2="355" class="wire"/>
|
||||
<rect x="794" y="335" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="800" y1="310" x2="800" y2="335" class="wire-pwr"/>
|
||||
<line x1="800" y1="355" x2="800" y2="370" class="wire"/>
|
||||
<polygon points="793,370 807,370 800,387" class="led-ir"/>
|
||||
<line x1="793" y1="387" x2="807" y2="387" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="800" y1="387" x2="800" y2="400" class="wire"/>
|
||||
<text x="790" y="328" class="pin" style="font-size:7px">R9</text>
|
||||
|
||||
<!-- LED R2 -->
|
||||
<line x1="835" y1="335" x2="835" y2="355" class="wire"/>
|
||||
<rect x="829" y="335" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="835" y1="310" x2="835" y2="335" class="wire-pwr"/>
|
||||
<line x1="835" y1="355" x2="835" y2="370" class="wire"/>
|
||||
<polygon points="828,370 842,370 835,387" class="led-ir"/>
|
||||
<line x1="828" y1="387" x2="842" y2="387" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="835" y1="387" x2="835" y2="400" class="wire"/>
|
||||
|
||||
<!-- LED R3 -->
|
||||
<line x1="870" y1="335" x2="870" y2="355" class="wire"/>
|
||||
<rect x="864" y="335" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="870" y1="310" x2="870" y2="335" class="wire-pwr"/>
|
||||
<line x1="870" y1="355" x2="870" y2="370" class="wire"/>
|
||||
<polygon points="863,370 877,370 870,387" class="led-ir"/>
|
||||
<line x1="863" y1="387" x2="877" y2="387" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="870" y1="387" x2="870" y2="400" class="wire"/>
|
||||
|
||||
<!-- LED R4 -->
|
||||
<line x1="905" y1="335" x2="905" y2="355" class="wire"/>
|
||||
<rect x="899" y="335" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="905" y1="310" x2="905" y2="335" class="wire-pwr"/>
|
||||
<line x1="905" y1="355" x2="905" y2="370" class="wire"/>
|
||||
<polygon points="898,370 912,370 905,387" class="led-ir"/>
|
||||
<line x1="898" y1="387" x2="912" y2="387" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="905" y1="387" x2="905" y2="400" class="wire"/>
|
||||
|
||||
<!-- LED R5 -->
|
||||
<line x1="940" y1="335" x2="940" y2="355" class="wire"/>
|
||||
<rect x="934" y="335" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="940" y1="310" x2="940" y2="335" class="wire-pwr"/>
|
||||
<line x1="940" y1="355" x2="940" y2="370" class="wire"/>
|
||||
<polygon points="933,370 947,370 940,387" class="led-ir"/>
|
||||
<line x1="933" y1="387" x2="947" y2="387" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="940" y1="387" x2="940" y2="400" class="wire"/>
|
||||
|
||||
<!-- LED R6 -->
|
||||
<line x1="975" y1="335" x2="975" y2="355" class="wire"/>
|
||||
<rect x="969" y="335" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="975" y1="310" x2="975" y2="335" class="wire-pwr"/>
|
||||
<line x1="975" y1="355" x2="975" y2="370" class="wire"/>
|
||||
<polygon points="968,370 982,370 975,387" class="led-ir"/>
|
||||
<line x1="968" y1="387" x2="982" y2="387" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="975" y1="387" x2="975" y2="400" class="wire"/>
|
||||
|
||||
<!-- LED R7 -->
|
||||
<line x1="1010" y1="335" x2="1010" y2="355" class="wire"/>
|
||||
<rect x="1004" y="335" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="1010" y1="310" x2="1010" y2="335" class="wire-pwr"/>
|
||||
<line x1="1010" y1="355" x2="1010" y2="370" class="wire"/>
|
||||
<polygon points="1003,370 1017,370 1010,387" class="led-ir"/>
|
||||
<line x1="1003" y1="387" x2="1017" y2="387" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="1010" y1="387" x2="1010" y2="400" class="wire"/>
|
||||
|
||||
<!-- LED R8 -->
|
||||
<line x1="1045" y1="335" x2="1045" y2="355" class="wire"/>
|
||||
<rect x="1039" y="335" width="12" height="25" class="comp-fill" rx="1.5"/>
|
||||
<line x1="1045" y1="310" x2="1045" y2="335" class="wire-pwr"/>
|
||||
<line x1="1045" y1="355" x2="1045" y2="370" class="wire"/>
|
||||
<polygon points="1038,370 1052,370 1045,387" class="led-ir"/>
|
||||
<line x1="1038" y1="387" x2="1052" y2="387" stroke="#b294bb" stroke-width="1.5"/>
|
||||
<line x1="1045" y1="387" x2="1045" y2="400" class="wire"/>
|
||||
|
||||
<!-- VCC bus for right group -->
|
||||
<line x1="800" y1="310" x2="1045" y2="310" class="wire-pwr"/>
|
||||
<line x1="920" y1="310" x2="920" y2="80" class="wire-pwr"/>
|
||||
<circle cx="920" cy="80" r="2.5" class="dot"/>
|
||||
<circle cx="920" cy="310" r="2.5" class="dot"/>
|
||||
|
||||
<!-- Common drain bus for right group -->
|
||||
<line x1="800" y1="400" x2="1045" y2="400" class="wire"/>
|
||||
<line x1="880" y1="400" x2="880" y2="435" class="wire"/>
|
||||
<circle cx="880" cy="400" r="2.5" class="dot"/>
|
||||
|
||||
<!-- IR glow -->
|
||||
<ellipse cx="920" cy="335" rx="160" ry="25" class="beam"/>
|
||||
|
||||
<!-- Label -->
|
||||
<text x="800" y="420" class="ref" style="font-size:10px">D9–D16: TSAL6200 (940nm, 8× parallel)</text>
|
||||
<text x="800" y="432" class="val">R9–R16: 47R (each: If = 51mA, total group = 408mA)</text>
|
||||
</g>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════ -->
|
||||
<!-- NOTES section (bottom right) -->
|
||||
<!-- ═══════════════════════════════════════════════════════ -->
|
||||
<rect x="740" y="610" width="640" height="210" class="section" rx="8"/>
|
||||
<text x="750" y="628" class="section-label">DESIGN NOTES</text>
|
||||
|
||||
<text x="760" y="650" class="val" style="font-size:11px;fill:#c5c8c6">Circuit Summary:</text>
|
||||
<text x="760" y="668" class="val" style="font-size:10px">
|
||||
ATtiny85 generates PWM on PB0/PB1 → gates two IRLML6344 N-MOSFETs
|
||||
</text>
|
||||
<text x="760" y="683" class="val" style="font-size:10px">
|
||||
Each MOSFET switches a bank of 8 IR LEDs (940nm) with individual 47R limiters
|
||||
</text>
|
||||
<text x="760" y="698" class="val" style="font-size:10px">
|
||||
Total max draw: 16 × 51mA = 816mA + MCU overhead ≈ 820mA
|
||||
</text>
|
||||
<text x="760" y="713" class="val" style="font-size:10px">
|
||||
Runtime at full power: 500mAh / 820mA ≈ 36 min
|
||||
</text>
|
||||
<text x="760" y="728" class="val" style="font-size:10px">
|
||||
Pulsed modes (30/60Hz, 50% duty) double runtime to ~72 min
|
||||
</text>
|
||||
|
||||
<text x="760" y="755" class="val" style="font-size:11px;fill:#c5c8c6">Component Count:</text>
|
||||
<text x="760" y="773" class="val" style="font-size:10px">
|
||||
1× ATtiny85 | 2× IRLML6344 | 16× TSAL6200 | 16× 47R | 3× misc R
|
||||
</text>
|
||||
<text x="760" y="788" class="val" style="font-size:10px">
|
||||
1× 100nF cap | 1× TP4056 module | 1× LiPo 500mAh | 2× switches | 1× status LED
|
||||
</text>
|
||||
<text x="760" y="803" class="val" style="font-size:10px;fill:#de935f">
|
||||
Estimated BOM cost: ~$15 USD (through-hole prototype)
|
||||
</text>
|
||||
|
||||
<!-- Decorative IR wave symbol in corner -->
|
||||
<g transform="translate(1300,660)" opacity="0.3">
|
||||
<circle cx="0" cy="0" r="10" stroke="#b294bb" stroke-width="1.5" fill="none"/>
|
||||
<circle cx="0" cy="0" r="20" stroke="#b294bb" stroke-width="1" fill="none"/>
|
||||
<circle cx="0" cy="0" r="30" stroke="#b294bb" stroke-width="0.5" fill="none"/>
|
||||
<text x="-5" y="5" style="font-size:12px;fill:#b294bb;font-weight:bold">IR</text>
|
||||
</g>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 31 KiB |
|
|
@ -0,0 +1,85 @@
|
|||
(kicad_pcb (version 20221018) (generator pcbnew)
|
||||
|
||||
(general
|
||||
(thickness 1.6)
|
||||
)
|
||||
|
||||
(paper "A4")
|
||||
(title_block
|
||||
(title "zk-glasses")
|
||||
(date "2026-02-21")
|
||||
)
|
||||
|
||||
(layers
|
||||
(0 "F.Cu" signal)
|
||||
(31 "B.Cu" signal)
|
||||
(32 "B.Adhes" user "B.Adhesive")
|
||||
(33 "F.Adhes" user "F.Adhesive")
|
||||
(34 "B.Paste" user)
|
||||
(35 "F.Paste" user)
|
||||
(36 "B.SilkS" user "B.Silkscreen")
|
||||
(37 "F.SilkS" user "F.Silkscreen")
|
||||
(38 "B.Mask" user)
|
||||
(39 "F.Mask" user)
|
||||
(40 "Dwgs.User" user "User.Drawings")
|
||||
(41 "Cmts.User" user "User.Comments")
|
||||
(42 "Eco1.User" user "User.Eco1")
|
||||
(43 "Eco2.User" user "User.Eco2")
|
||||
(44 "Edge.Cuts" user)
|
||||
(45 "Margin" user)
|
||||
(46 "B.CrtYd" user "B.Courtyard")
|
||||
(47 "F.CrtYd" user "F.Courtyard")
|
||||
(48 "B.Fab" user)
|
||||
(49 "F.Fab" user)
|
||||
(50 "User.1" user)
|
||||
(51 "User.2" user)
|
||||
(52 "User.3" user)
|
||||
(53 "User.4" user)
|
||||
(54 "User.5" user)
|
||||
(55 "User.6" user)
|
||||
(56 "User.7" user)
|
||||
(57 "User.8" user)
|
||||
(58 "User.9" user)
|
||||
)
|
||||
|
||||
(setup
|
||||
(pad_to_mask_clearance 0)
|
||||
(pcbplotparams
|
||||
(layerselection 0x00010fc_ffffffff)
|
||||
(plot_on_all_layers_selection 0x0000000_00000000)
|
||||
(disableapertmacros false)
|
||||
(usegerberextensions false)
|
||||
(usegerberattributes true)
|
||||
(usegerberadvancedattributes true)
|
||||
(creategerberjobfile true)
|
||||
(dashed_line_dash_ratio 12.000000)
|
||||
(dashed_line_gap_ratio 3.000000)
|
||||
(svgprecision 4)
|
||||
(plotframeref false)
|
||||
(viasonmask false)
|
||||
(mode 1)
|
||||
(useauxorigin false)
|
||||
(hpglpennumber 1)
|
||||
(hpglpenspeed 20)
|
||||
(hpglpendiameter 15.000000)
|
||||
(dxfpolygonmode true)
|
||||
(dxfimperialunits true)
|
||||
(dxfusepcbnewfont true)
|
||||
(psnegative false)
|
||||
(psa4output false)
|
||||
(plotreference true)
|
||||
(plotvalue true)
|
||||
(plotinvisibletext false)
|
||||
(sketchpadsonfab false)
|
||||
(subtractmaskfromsilk false)
|
||||
(outputformat 1)
|
||||
(mirror false)
|
||||
(drillshape 1)
|
||||
(scaleselection 1)
|
||||
(outputdirectory "")
|
||||
)
|
||||
)
|
||||
|
||||
(net 0 "")
|
||||
|
||||
)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"board": {
|
||||
"filename": "zk-glasses.kicad_pcb"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
(kicad_sch (version 20230121) (generator "KiCAD-MCP-Server"))
|
||||