Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 46a2bec551 | |||
| 251f7ed376 |
@ -0,0 +1,311 @@
|
||||
/*
|
||||
=============================================================================
|
||||
FireFly Morse Throwie
|
||||
- a light controlled (LED as Sensor) morse blinker throwie with ATTiny85
|
||||
=============================================================================
|
||||
|
||||
Project definitions, sources
|
||||
-----------------------------------------------------------------------------
|
||||
Version: 0.2 - ATTiny85, 1 MHz, BOD fuse disabled
|
||||
gitea : https://gitea.togo-lab.io/tgohle/0001-FireFly
|
||||
Date : 2026-06-13
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
Inspired by Karl Lunt's FireFly project:
|
||||
http://www.seanet.com/~karllunt/fireflyLED.html
|
||||
|
||||
Morse code reference:
|
||||
'A', ".-" 'B', "-..." 'C', "-.-."
|
||||
'D', "-.." 'E', "." 'F', "..-."
|
||||
'G', "--." 'H', "...." 'I', ".."
|
||||
'J', ".---" 'K', "-.-" 'L', ".-.."
|
||||
'M', "--" 'N', "-." 'O', "---"
|
||||
'P', ".--." 'Q', "--.-" 'R', ".-."
|
||||
'S', "..." 'T', "-" 'U', "..-"
|
||||
'V', "...-" 'W', ".--" 'X', "-..-"
|
||||
'Y', "-.--" 'Z', "--.."
|
||||
|
||||
'1', ".----" '2', "..---" '3', "...--"
|
||||
'4', "....-" '5', "....." '6', "-...."
|
||||
'7', "--..." '8', "---.." '9', "----."
|
||||
'0', "-----"
|
||||
|
||||
'.', ".-.-.-" ',', "--..--" '?', "..--.."
|
||||
'!', "-.-.--" ':', "---..." ';', "-.-.-."
|
||||
'(', "-.--." ')', "-.--.-" '"', ".-..-."
|
||||
'@', ".--.-." '&', ".-..."
|
||||
|
||||
Legal stuff / Copyright:
|
||||
License_-_CC_BY-NC_4.0
|
||||
https://creativecommons.org/licenses/by-nc/4.0/
|
||||
-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <avr/sleep.h>
|
||||
#include <avr/wdt.h>
|
||||
#include <avr/pgmspace.h> // FIX 3: needed for PROGMEM / pgm_read_byte
|
||||
|
||||
#ifndef cbi
|
||||
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
||||
#endif
|
||||
#ifndef sbi
|
||||
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timing: unit length in ms.
|
||||
// At 1 MHz, delay() is accurate when F_CPU=1000000L is set in boards.txt.
|
||||
// 100 ms gives readable optical Morse; raise to 150 if readability is poor.
|
||||
#define unitLength 100
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LED pin definitions (N-side = cathode, P-side = anode for sensing/driving)
|
||||
#define LED1_N_SIDE 3
|
||||
#define LED1_P_SIDE 4
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ATTiny85 has 512 bytes SRAM; this frees all of it for the stack.
|
||||
// Only uppercase letters, digits, and the special chars in the switch below.
|
||||
// Test only: 20 x "0" due 0 = "-----" most energy draining
|
||||
const char morseText[] PROGMEM = "0000000000000000000";
|
||||
// Ruler: 0...0....1...1....2 Attention: more Text
|
||||
// Ruler: 1...5....0...5....0 will cost more power!
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Darkness threshold.
|
||||
// Higher value = triggers in brighter conditions.
|
||||
// Best calibrated at actual dusk/dawn with the chosen LED type.
|
||||
unsigned int darknessThreshold = 17000;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Watchdog interrupt flag — volatile because it is written in an ISR
|
||||
volatile boolean f_wdt = 1;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Forward declarations
|
||||
void setup_watchdog(int ii);
|
||||
void system_sleep();
|
||||
unsigned int sensDarkness(int LED_N, int LED_P);
|
||||
void morse(int LED_N, int LED_P);
|
||||
void dit(int LED_P);
|
||||
void dah(int LED_P);
|
||||
|
||||
// ===========================================================================
|
||||
// Setup
|
||||
// ===========================================================================
|
||||
void setup()
|
||||
{
|
||||
// Disable unused peripherals immediately to save some µ/mA.
|
||||
// PRTIM1, PRUSI: genuinely unused, safe to gate off
|
||||
// PRADC: controlled manually around sensDarkness()
|
||||
// PRTIM0: must stay ON — delay() and millis() depend on Timer0
|
||||
PRR = (1 << PRTIM1) | (1 << PRUSI) | (1 << PRADC);
|
||||
|
||||
// ADC is gated via PRR above; also clear ADEN just in case
|
||||
cbi(ADCSRA, ADEN);
|
||||
|
||||
setup_watchdog(9); // 8-second watchdog interval
|
||||
// simple repeat if more delay is need, eg 4
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Main loop
|
||||
// ===========================================================================
|
||||
void loop()
|
||||
{
|
||||
if (f_wdt == 1) {
|
||||
f_wdt = 0;
|
||||
|
||||
// Enable ADC only for the sensing window, then shut it off again.
|
||||
cbi(PRR, PRADC); // un-gate ADC clock
|
||||
sbi(ADCSRA, ADEN); // power ADC on
|
||||
|
||||
if (sensDarkness(LED1_N_SIDE, LED1_P_SIDE) > darknessThreshold) {
|
||||
morse(LED1_N_SIDE, LED1_P_SIDE);
|
||||
}
|
||||
|
||||
cbi(ADCSRA, ADEN); // ADC off
|
||||
sbi(PRR, PRADC); // re-gate ADC clock
|
||||
|
||||
// Return LED pins to input (high-Z) before sleeping
|
||||
pinMode(LED1_N_SIDE, INPUT);
|
||||
pinMode(LED1_P_SIDE, INPUT);
|
||||
}
|
||||
|
||||
// sleep 4 x 8 s = ~32 s between activations
|
||||
for (uint8_t i = 0; i < 1; i++) {
|
||||
system_sleep();
|
||||
}
|
||||
// f_wdt is set to 1 by the WDT ISR when the 8 s expire; no manual clear needed here.
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Sleep helpers
|
||||
// ===========================================================================
|
||||
|
||||
void system_sleep()
|
||||
{
|
||||
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
||||
sleep_enable();
|
||||
sleep_mode(); // CPU halts here until WDT fires
|
||||
sleep_disable();
|
||||
// ADC stays off - re-enable it in loop() only when sensing
|
||||
}
|
||||
|
||||
// Watchdog setup - ii selects timeout:
|
||||
// 0=16ms 1=32ms 2=64ms 3=128ms 4=250ms 5=500ms
|
||||
// 6=1s 7=2s 8=4s 9=8s
|
||||
void setup_watchdog(int ii)
|
||||
{
|
||||
byte bb;
|
||||
if (ii > 9) ii = 9;
|
||||
bb = ii & 7;
|
||||
if (ii > 7) bb |= (1 << 5);
|
||||
bb |= (1 << WDCE);
|
||||
|
||||
MCUSR &= ~(1 << WDRF);
|
||||
WDTCR |= (1 << WDCE) | (1 << WDE); // timed sequence - must not be split
|
||||
WDTCR = bb;
|
||||
WDTCR |= _BV(WDIE);
|
||||
}
|
||||
|
||||
ISR(WDT_vect)
|
||||
{
|
||||
f_wdt = 1;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Light sensor
|
||||
// ===========================================================================
|
||||
// Returns a "darkness level": higher = darker.
|
||||
// Charges the LED junction capacitor, then times how long it takes to bleed
|
||||
// back through the reverse-biased diode to a logic LOW.
|
||||
// ~30000 = pitch black, ~0 = bright light
|
||||
|
||||
// return type is unsigned int (counter can reach 30000, fits in 16 bits)
|
||||
unsigned int sensDarkness(int LED_N, int LED_P)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
// Charge the LED (forward-bias momentarily)
|
||||
pinMode(LED_N, OUTPUT);
|
||||
pinMode(LED_P, OUTPUT);
|
||||
digitalWrite(LED_N, HIGH);
|
||||
digitalWrite(LED_P, LOW);
|
||||
|
||||
// Let the N-end float and measure bleed-down time
|
||||
pinMode(LED_N, INPUT);
|
||||
digitalWrite(LED_N, LOW); // disable internal pull-up
|
||||
|
||||
for (i = 0; i < 30000; i++) {
|
||||
if (digitalRead(LED_N) == 0) break;
|
||||
}
|
||||
|
||||
// clean up after sensing
|
||||
pinMode(LED_N, OUTPUT);
|
||||
digitalWrite(LED_N, LOW);
|
||||
pinMode(LED_P, OUTPUT);
|
||||
digitalWrite(LED_P, LOW);
|
||||
return i;
|
||||
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Morse helpers
|
||||
// ===========================================================================
|
||||
|
||||
void dit(int LED_P)
|
||||
{
|
||||
digitalWrite(LED_P, HIGH); delay(unitLength);
|
||||
digitalWrite(LED_P, LOW); delay(unitLength);
|
||||
}
|
||||
|
||||
void dah(int LED_P)
|
||||
{
|
||||
digitalWrite(LED_P, HIGH); delay(unitLength * 3);
|
||||
digitalWrite(LED_P, LOW); delay(unitLength);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Morse sender
|
||||
// ===========================================================================
|
||||
void morse(int LED_N, int LED_P)
|
||||
{
|
||||
pinMode(LED_N, OUTPUT);
|
||||
pinMode(LED_P, OUTPUT);
|
||||
digitalWrite(LED_N, LOW);
|
||||
|
||||
// read each character from flash with pgm_read_byte()
|
||||
// use < morseText length, not <= (avoids reading past the null terminator)
|
||||
uint8_t len = strlen_P(morseText);
|
||||
for (uint8_t i = 0; i < len; i++)
|
||||
{
|
||||
char c = (char)pgm_read_byte(&morseText[i]);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
// ----- Letters -----
|
||||
case 'A': dit(LED_P); dah(LED_P); break; // .-
|
||||
case 'B': dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // -...
|
||||
case 'C': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // -.-.
|
||||
case 'D': dah(LED_P); dit(LED_P); dit(LED_P); break; // -..
|
||||
case 'E': dit(LED_P); break; // .
|
||||
case 'F': dit(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // ..-.
|
||||
case 'G': dah(LED_P); dah(LED_P); dit(LED_P); break; // --.
|
||||
case 'H': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // ....
|
||||
case 'I': dit(LED_P); dit(LED_P); break; // ..
|
||||
case 'J': dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // .---
|
||||
case 'K': dah(LED_P); dit(LED_P); dah(LED_P); break; // -.-
|
||||
case 'L': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // .-..
|
||||
case 'M': dah(LED_P); dah(LED_P); break; // --
|
||||
case 'N': dah(LED_P); dit(LED_P); break; // -.
|
||||
case 'O': dah(LED_P); dah(LED_P); dah(LED_P); break; // ---
|
||||
case 'P': dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); break; // .--.
|
||||
case 'Q': dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); break; // --.-
|
||||
case 'R': dit(LED_P); dah(LED_P); dit(LED_P); break; // .-.
|
||||
case 'S': dit(LED_P); dit(LED_P); dit(LED_P); break; // ...
|
||||
case 'T': dah(LED_P); break; // -
|
||||
case 'U': dit(LED_P); dit(LED_P); dah(LED_P); break; // ..-
|
||||
case 'V': dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); break; // ...-
|
||||
case 'W': dit(LED_P); dah(LED_P); dah(LED_P); break; // .--
|
||||
case 'X': dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); break; // -..-
|
||||
case 'Y': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // -.--
|
||||
case 'Z': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // --..
|
||||
|
||||
// ----- Digits -----
|
||||
case '1': dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // .----
|
||||
case '2': dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // ..---
|
||||
case '3': dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // ...--
|
||||
case '4': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); break; // ....-
|
||||
case '5': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // .....
|
||||
case '6': dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // -....
|
||||
case '7': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // --...
|
||||
case '8': dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // ---.
|
||||
case '9': dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); break; // ----.
|
||||
case '0': dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // -----
|
||||
|
||||
// ----- Punctuation -----
|
||||
case ' ': delay(unitLength * 5); break; // word gap (5 + 3 = 8 units total)
|
||||
case '.': dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); break; // .-.-.-
|
||||
case ',': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // --..--
|
||||
case '?': dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // ..--..
|
||||
case '!': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // -.-.--
|
||||
case ':': dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // ---...
|
||||
case ';': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // -.-.-.
|
||||
case '(': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); break; // -.--.
|
||||
case ')': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); break; // -.--.-
|
||||
case '"': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // .-..-.
|
||||
case '@': dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // .--.-.
|
||||
case '&': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // .-...
|
||||
}
|
||||
|
||||
// Inter-character gap: 3 units (the dit/dah functions already trail 1 unit,
|
||||
// so this adds the remaining 2 to reach the standard 3-unit gap).
|
||||
// Space case already handles word gap - no extra delay needed there.
|
||||
if (c != ' ') {
|
||||
delay(unitLength * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -183,8 +183,8 @@
|
||||
// theoretical you can use up to 3 LED. For example you can use it for a
|
||||
// landmark beacon. So if you point this LEDs to different directions you can
|
||||
// detect the position in dependence of your location to the bacon.
|
||||
#define LED1_N_SIDE 4
|
||||
#define LED1_P_SIDE 3
|
||||
#define LED1_N_SIDE 3
|
||||
#define LED1_P_SIDE 4
|
||||
|
||||
// ===========================================================================
|
||||
// Variables
|
||||
|
||||
Binary file not shown.
@ -1,288 +0,0 @@
|
||||
/*
|
||||
FireFly tetrahedron adapter
|
||||
Base STL: tetrahedron40mmC.stl
|
||||
|
||||
Purpose:
|
||||
- Import original 40 mm foldable tetrahedron STL
|
||||
- Scale it to 60 mm base geometry
|
||||
- Add cable holes, vent/drain holes, and optional PCB mounting holes
|
||||
|
||||
Workflow:
|
||||
1. Set PART = "upper"
|
||||
2. Render with F6
|
||||
3. Export STL
|
||||
4. Set PART = "lower"
|
||||
5. Render with F6
|
||||
6. Export STL
|
||||
|
||||
Notes:
|
||||
- Hole coordinates are defined on the original 40 mm STL coordinate system.
|
||||
- The script scales them automatically to 60 mm.
|
||||
- First export should be treated as v0.1 prototype.
|
||||
*/
|
||||
|
||||
|
||||
/* =========================
|
||||
Main selection
|
||||
========================= */
|
||||
|
||||
PART = "upper"; // "upper", "lower", or "both_preview"
|
||||
|
||||
|
||||
/* =========================
|
||||
Input file
|
||||
========================= */
|
||||
|
||||
input_stl = "tetrahedron40mmC.stl";
|
||||
|
||||
|
||||
/* =========================
|
||||
Scaling
|
||||
========================= */
|
||||
|
||||
// Original tetrahedron edge length
|
||||
original_edge_mm = 40;
|
||||
|
||||
// Target mechanical base size
|
||||
target_edge_mm = 60;
|
||||
|
||||
// XY scale from 40 mm to 60 mm
|
||||
scale_xy = target_edge_mm / original_edge_mm; // 1.5
|
||||
|
||||
// Keep original thickness.
|
||||
// Set to 1.2 or 1.3 if you want a slightly thicker part.
|
||||
scale_z = 1.0;
|
||||
|
||||
|
||||
/* =========================
|
||||
General hole settings
|
||||
========================= */
|
||||
|
||||
hole_fn = 40;
|
||||
cut_height = 20;
|
||||
|
||||
|
||||
/* =========================
|
||||
Feature switches
|
||||
========================= */
|
||||
|
||||
add_upper_cable_holes = true;
|
||||
add_upper_hanger_hole = true;
|
||||
|
||||
add_lower_vent_holes = true;
|
||||
add_lower_cable_holes = true;
|
||||
add_lower_pcb_mount_holes = true;
|
||||
|
||||
// Keep this false for the first prototype.
|
||||
// Standoffs may interfere with folding depending on print orientation.
|
||||
add_lower_pcb_standoffs = false;
|
||||
|
||||
|
||||
/* =========================
|
||||
Hole sizes
|
||||
========================= */
|
||||
|
||||
cable_hole_d = 3.2; // for small solar-panel wires
|
||||
hanger_hole_d = 3.5; // for small ring / wire / eyelet
|
||||
vent_hole_d = 2.0; // condensation vent / drain holes
|
||||
pcb_screw_hole_d = 2.2; // M2 clearance-ish
|
||||
pcb_standoff_d = 6.0;
|
||||
pcb_standoff_h = 3.0;
|
||||
|
||||
|
||||
/* =========================
|
||||
Coordinate reference
|
||||
=========================
|
||||
|
||||
The uploaded STL is approximately:
|
||||
|
||||
X: -34.64 to +34.64
|
||||
Y: -20.00 to +60.00
|
||||
Z: -0.10 to +2.00
|
||||
|
||||
The original model is a flat foldable tetrahedron net.
|
||||
Coordinates below are before scaling.
|
||||
*/
|
||||
|
||||
|
||||
/* =========================
|
||||
Upper part hole positions
|
||||
========================= */
|
||||
|
||||
// Cable holes for three solar panels.
|
||||
// Adjust after checking where your real panel wires exit.
|
||||
upper_cable_points = [
|
||||
[-18, 20],
|
||||
[ 24, 7],
|
||||
[ 24, 33]
|
||||
];
|
||||
|
||||
// Optional hanger hole close to one upper fold/vertex area.
|
||||
// If this weakens the corner too much, disable it and use an external loop instead.
|
||||
upper_hanger_points = [
|
||||
[0, 38]
|
||||
];
|
||||
|
||||
|
||||
/* =========================
|
||||
Lower part hole positions
|
||||
========================= */
|
||||
|
||||
// Cable transfer holes between upper solar section and lower electronics bay.
|
||||
lower_cable_points = [
|
||||
[-10, 20],
|
||||
[ 12, 13],
|
||||
[ 12, 27]
|
||||
];
|
||||
|
||||
// Small vent/drain hole clusters near likely lower/edge areas.
|
||||
// These are deliberately small.
|
||||
lower_vent_centres = [
|
||||
[ 0, 3],
|
||||
[ 0, 37],
|
||||
[-30, 20],
|
||||
[ 31, 20]
|
||||
];
|
||||
|
||||
// Approximate triangular PCB mounting pattern.
|
||||
// Tune this after measuring your actual red triangle PCB.
|
||||
lower_pcb_mount_points = [
|
||||
[ 0, 8],
|
||||
[ 25, 20],
|
||||
[ 0, 32]
|
||||
];
|
||||
|
||||
|
||||
/* =========================
|
||||
Basic modules
|
||||
========================= */
|
||||
|
||||
module base_import_scaled()
|
||||
{
|
||||
scale([scale_xy, scale_xy, scale_z])
|
||||
import(input_stl, convexity = 10);
|
||||
}
|
||||
|
||||
|
||||
module vertical_hole(p, d)
|
||||
{
|
||||
translate([p[0] * scale_xy, p[1] * scale_xy, 1])
|
||||
cylinder(h = cut_height, d = d, center = true, $fn = hole_fn);
|
||||
}
|
||||
|
||||
|
||||
module vent_cluster(p)
|
||||
{
|
||||
// Small 2 x 3 vent/drain pattern.
|
||||
// Pitch is in final millimetres, not original model coordinates.
|
||||
pitch = 3.0;
|
||||
|
||||
for (ix = [-1, 0, 1])
|
||||
for (iy = [0, 1])
|
||||
translate([
|
||||
p[0] * scale_xy + ix * pitch,
|
||||
p[1] * scale_xy + iy * pitch,
|
||||
1
|
||||
])
|
||||
cylinder(h = cut_height, d = vent_hole_d, center = true, $fn = hole_fn);
|
||||
}
|
||||
|
||||
|
||||
module pcb_standoff(p)
|
||||
{
|
||||
// Standoff added on top of the flat STL.
|
||||
// Use only if you confirm it does not block folding.
|
||||
translate([p[0] * scale_xy, p[1] * scale_xy, 2.1 * scale_z])
|
||||
cylinder(h = pcb_standoff_h, d = pcb_standoff_d, center = false, $fn = hole_fn);
|
||||
}
|
||||
|
||||
|
||||
/* =========================
|
||||
Upper tetrahedron
|
||||
========================= */
|
||||
|
||||
module upper_part()
|
||||
{
|
||||
difference()
|
||||
{
|
||||
base_import_scaled();
|
||||
|
||||
if (add_upper_cable_holes)
|
||||
{
|
||||
for (p = upper_cable_points)
|
||||
vertical_hole(p, cable_hole_d);
|
||||
}
|
||||
|
||||
if (add_upper_hanger_hole)
|
||||
{
|
||||
for (p = upper_hanger_points)
|
||||
vertical_hole(p, hanger_hole_d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* =========================
|
||||
Lower tetrahedron
|
||||
========================= */
|
||||
|
||||
module lower_part()
|
||||
{
|
||||
difference()
|
||||
{
|
||||
union()
|
||||
{
|
||||
base_import_scaled();
|
||||
|
||||
if (add_lower_pcb_standoffs)
|
||||
{
|
||||
for (p = lower_pcb_mount_points)
|
||||
pcb_standoff(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (add_lower_cable_holes)
|
||||
{
|
||||
for (p = lower_cable_points)
|
||||
vertical_hole(p, cable_hole_d);
|
||||
}
|
||||
|
||||
if (add_lower_vent_holes)
|
||||
{
|
||||
for (p = lower_vent_centres)
|
||||
vent_cluster(p);
|
||||
}
|
||||
|
||||
if (add_lower_pcb_mount_holes)
|
||||
{
|
||||
for (p = lower_pcb_mount_points)
|
||||
vertical_hole(p, pcb_screw_hole_d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* =========================
|
||||
Output
|
||||
========================= */
|
||||
|
||||
if (PART == "upper")
|
||||
{
|
||||
upper_part();
|
||||
}
|
||||
else if (PART == "lower")
|
||||
{
|
||||
lower_part();
|
||||
}
|
||||
else if (PART == "both_preview")
|
||||
{
|
||||
translate([-80, 0, 0])
|
||||
upper_part();
|
||||
|
||||
translate([80, 0, 0])
|
||||
lower_part();
|
||||
}
|
||||
else
|
||||
{
|
||||
echo("Invalid PART setting. Use upper, lower, or both_preview.");
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
7960
hardware/CAD/ToGo-Lab_FireFly_MorseThrowie - LaserCut.dxf
Normal file
7960
hardware/CAD/ToGo-Lab_FireFly_MorseThrowie - LaserCut.dxf
Normal file
File diff suppressed because it is too large
Load Diff
7410
hardware/CAD/ToGo-Lab_FireFly_MorseThrowie - LaserCut_Upload.dxf
Normal file
7410
hardware/CAD/ToGo-Lab_FireFly_MorseThrowie - LaserCut_Upload.dxf
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user