Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ebf338c97f | |||
| 46a2bec551 | |||
| 251f7ed376 | |||
| 23fae3e290 | |||
| b0ba83ca4b |
@ -0,0 +1,391 @@
|
|||||||
|
/*
|
||||||
|
=============================================================================
|
||||||
|
FireFly Morse Throwie
|
||||||
|
- a light controlled (LED as Sensor) morse blinker throwie with ATTiny85
|
||||||
|
=============================================================================
|
||||||
|
|
||||||
|
Project definitions, sources
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
Version: 0.3 - ATTiny85, 1 MHz, BOD fuse disabled
|
||||||
|
gitea : https://gitea.togo-lab.io/tgohle/0001-FireFly
|
||||||
|
Date : 2026-06-14
|
||||||
|
|
||||||
|
Key changes Version 0.3:
|
||||||
|
|
||||||
|
- Removed unnecessary ADC enable/disable from loop()
|
||||||
|
- Disabled ADC and analog comparator in setup()
|
||||||
|
- Added INPUT_PULLUP for unused Arduino pins 0, 1, 2 will save power
|
||||||
|
- Added safer watchdog setup with interrupt protection to save run near brownout
|
||||||
|
- Added wdt_reset() before sleep for cleaner 8 s timing
|
||||||
|
- Added lowercase-to-uppercase handling
|
||||||
|
- Unsupported characters are now ignored without adding fake timing gaps
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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 ms if readability is poor.
|
||||||
|
#define unitLength 100
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Sleep interval.
|
||||||
|
// Watchdog setup uses 8 s. For final use, change sleepCycles from 1 to 4
|
||||||
|
// for about 32 s between sensing/blinking activations.
|
||||||
|
const uint8_t sleepCycles = 4;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// LED pin definitions (N-side = cathode, P-side = anode for sensing/driving).
|
||||||
|
#define LED1_N_SIDE 3
|
||||||
|
#define LED1_P_SIDE 4
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ATTiny85 Arduino pins 0, 1, 2 are unused in actual design and physically open.
|
||||||
|
// Keep them in INPUT_PULLUP so they do not float and waste current.
|
||||||
|
#define UNUSED_PIN_0 0
|
||||||
|
#define UNUSED_PIN_1 1
|
||||||
|
#define UNUSED_PIN_2 2
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ATTiny85 has 512 bytes SRAM; this keeps the Morse text in flash.
|
||||||
|
// Only uppercase letters, digits, spaces, and the special chars in the switch
|
||||||
|
// below are emitted. Lowercase letters are converted to uppercase.
|
||||||
|
// Test setup: 20 x "0" because 0 = "-----" gives maximum LED ON time for test.
|
||||||
|
// shorter text will better, more text will cost more power.
|
||||||
|
const char morseText[] PROGMEM = "TOGO LAB";
|
||||||
|
// Ruler: 0....0....1...1....2
|
||||||
|
// Ruler: 0....5....0...5....0
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Darkness threshold.
|
||||||
|
// Higher value = triggers in brighter conditions.
|
||||||
|
// Best calibrated at actual dusk/dawn with the chosen LED type.
|
||||||
|
const unsigned int darknessThreshold = 17000;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Watchdog interrupt flag — volatile because it is written in an ISR.
|
||||||
|
volatile bool f_wdt = true;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Forward declarations.
|
||||||
|
void setup_watchdog(uint8_t ii);
|
||||||
|
void system_sleep();
|
||||||
|
void configureUnusedPins();
|
||||||
|
void releaseLedPins(int LED_N, int LED_P);
|
||||||
|
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 power.
|
||||||
|
// PRTIM1, PRUSI: unused, safe to gate off.
|
||||||
|
// PRADC: ADC is not used; LED sensing is done with digitalRead().
|
||||||
|
// PRTIM0: must stay ON while awake because delay() depends on Timer0.
|
||||||
|
PRR = (1 << PRTIM1) | (1 << PRUSI) | (1 << PRADC);
|
||||||
|
|
||||||
|
// ADC off. It is not needed for digitalRead()-based LED sensing.
|
||||||
|
cbi(ADCSRA, ADEN);
|
||||||
|
|
||||||
|
// Analog comparator off. Saves a little sleep current.
|
||||||
|
ACSR |= (1 << ACD);
|
||||||
|
|
||||||
|
configureUnusedPins();
|
||||||
|
releaseLedPins(LED1_N_SIDE, LED1_P_SIDE);
|
||||||
|
|
||||||
|
setup_watchdog(9); // 8-second watchdog interval.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Main loop
|
||||||
|
// ===========================================================================
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
if (f_wdt) {
|
||||||
|
f_wdt = false;
|
||||||
|
|
||||||
|
if (sensDarkness(LED1_N_SIDE, LED1_P_SIDE) > darknessThreshold) {
|
||||||
|
morse(LED1_N_SIDE, LED1_P_SIDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return LED pins to high-Z before sleeping.
|
||||||
|
// Internal pullups stay disabled for the LED pins.
|
||||||
|
releaseLedPins(LED1_N_SIDE, LED1_P_SIDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test setup: 1 x 8 s sleep.
|
||||||
|
// Final setup: set sleepCycles = 4 for about 32 s.
|
||||||
|
for (uint8_t i = 0; i < sleepCycles; i++) {
|
||||||
|
system_sleep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Pin helpers
|
||||||
|
// ===========================================================================
|
||||||
|
void configureUnusedPins()
|
||||||
|
{
|
||||||
|
pinMode(UNUSED_PIN_0, INPUT_PULLUP);
|
||||||
|
pinMode(UNUSED_PIN_1, INPUT_PULLUP);
|
||||||
|
pinMode(UNUSED_PIN_2, INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void releaseLedPins(int LED_N, int LED_P)
|
||||||
|
{
|
||||||
|
// digitalWrite LOW before INPUT disables the internal pullup on Arduino cores.
|
||||||
|
digitalWrite(LED_N, LOW);
|
||||||
|
digitalWrite(LED_P, LOW);
|
||||||
|
pinMode(LED_N, INPUT);
|
||||||
|
pinMode(LED_P, INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Sleep helpers
|
||||||
|
// ===========================================================================
|
||||||
|
void system_sleep()
|
||||||
|
{
|
||||||
|
// Reset watchdog counter so each sleep cycle starts with a fresh interval.
|
||||||
|
wdt_reset();
|
||||||
|
|
||||||
|
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
||||||
|
|
||||||
|
// Atomic sleep entry pattern.
|
||||||
|
// After sei(), AVR executes the next instruction before servicing interrupts,
|
||||||
|
// so sleep_cpu() is not skipped by a just-pending interrupt.
|
||||||
|
cli();
|
||||||
|
sleep_enable();
|
||||||
|
sei();
|
||||||
|
sleep_cpu();
|
||||||
|
|
||||||
|
// WDT interrupt wakes the CPU here.
|
||||||
|
sleep_disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(uint8_t ii)
|
||||||
|
{
|
||||||
|
if (ii > 9) ii = 9;
|
||||||
|
|
||||||
|
uint8_t bb = ii & 7;
|
||||||
|
if (ii > 7) bb |= (1 << WDP3);
|
||||||
|
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli();
|
||||||
|
|
||||||
|
wdt_reset();
|
||||||
|
MCUSR &= ~(1 << WDRF);
|
||||||
|
|
||||||
|
// Timed sequence: enable configuration change, then set interrupt-only WDT.
|
||||||
|
WDTCR |= (1 << WDCE) | (1 << WDE);
|
||||||
|
WDTCR = bb | (1 << WDIE);
|
||||||
|
|
||||||
|
SREG = oldSREG;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISR(WDT_vect)
|
||||||
|
{
|
||||||
|
f_wdt = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Light sensor
|
||||||
|
// ===========================================================================
|
||||||
|
// Returns a "darkness level": higher = darker.
|
||||||
|
// Charges the LED junction capacitance, then times how long it takes to bleed
|
||||||
|
// back through the reverse-biased LED until the input reads LOW.
|
||||||
|
// ~30000 = pitch black, ~0 = bright light
|
||||||
|
unsigned int sensDarkness(int LED_N, int LED_P)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
// Charge the LED junction capacitance.
|
||||||
|
pinMode(LED_N, OUTPUT);
|
||||||
|
pinMode(LED_P, OUTPUT);
|
||||||
|
digitalWrite(LED_N, HIGH);
|
||||||
|
digitalWrite(LED_P, LOW);
|
||||||
|
|
||||||
|
// Let the N-side float and measure bleed-down time.
|
||||||
|
pinMode(LED_N, INPUT);
|
||||||
|
digitalWrite(LED_N, LOW); // disable internal pullup
|
||||||
|
|
||||||
|
for (i = 0; i < 30000; i++) {
|
||||||
|
if (digitalRead(LED_N) == LOW) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up after sensing: both LED pins low, then caller releases them.
|
||||||
|
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);
|
||||||
|
digitalWrite(LED_P, LOW);
|
||||||
|
|
||||||
|
const size_t len = strlen_P(morseText);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
char c = (char)pgm_read_byte(&morseText[i]);
|
||||||
|
|
||||||
|
// Allow lowercase source text without silently breaking timing.
|
||||||
|
if (c >= 'a' && c <= 'z') {
|
||||||
|
c = c - 'a' + 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool symbolSent = false;
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
// ----- Letters -----
|
||||||
|
case 'A': dit(LED_P); dah(LED_P); symbolSent = true; break; // .-
|
||||||
|
case 'B': dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // -...
|
||||||
|
case 'C': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // -.-.
|
||||||
|
case 'D': dah(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // -..
|
||||||
|
case 'E': dit(LED_P); symbolSent = true; break; // .
|
||||||
|
case 'F': dit(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // ..-.
|
||||||
|
case 'G': dah(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // --.
|
||||||
|
case 'H': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // ....
|
||||||
|
case 'I': dit(LED_P); dit(LED_P); symbolSent = true; break; // ..
|
||||||
|
case 'J': dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // .---
|
||||||
|
case 'K': dah(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // -.-
|
||||||
|
case 'L': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // .-..
|
||||||
|
case 'M': dah(LED_P); dah(LED_P); symbolSent = true; break; // --
|
||||||
|
case 'N': dah(LED_P); dit(LED_P); symbolSent = true; break; // -.
|
||||||
|
case 'O': dah(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // ---
|
||||||
|
case 'P': dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // .--.
|
||||||
|
case 'Q': dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // --.-
|
||||||
|
case 'R': dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // .-.
|
||||||
|
case 'S': dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // ...
|
||||||
|
case 'T': dah(LED_P); symbolSent = true; break; // -
|
||||||
|
case 'U': dit(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // ..-
|
||||||
|
case 'V': dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // ...-
|
||||||
|
case 'W': dit(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // .--
|
||||||
|
case 'X': dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // -..-
|
||||||
|
case 'Y': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // -.--
|
||||||
|
case 'Z': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // --..
|
||||||
|
|
||||||
|
// ----- Digits -----
|
||||||
|
case '1': dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // .----
|
||||||
|
case '2': dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // ..---
|
||||||
|
case '3': dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // ...--
|
||||||
|
case '4': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // ....-
|
||||||
|
case '5': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // .....
|
||||||
|
case '6': dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // -....
|
||||||
|
case '7': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // --...
|
||||||
|
case '8': dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // ---..
|
||||||
|
case '9': dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // ----.
|
||||||
|
case '0': dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // -----
|
||||||
|
|
||||||
|
// ----- Punctuation -----
|
||||||
|
case '.': dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // .-.-.-
|
||||||
|
case ',': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // --..--
|
||||||
|
case '?': dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // ..--..
|
||||||
|
case '!': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // -.-.--
|
||||||
|
case ':': dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // ---...
|
||||||
|
case ';': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // -.-.-.
|
||||||
|
case '(': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // -.--.
|
||||||
|
case ')': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // -.--.-
|
||||||
|
case '"': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // .-..-.
|
||||||
|
case '@': dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // .--.-.
|
||||||
|
case '&': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // .-...
|
||||||
|
|
||||||
|
// Space handling:
|
||||||
|
// Previous symbol already ended with 1 unit OFF and then received the
|
||||||
|
// normal 2 unit character gap. Add 4 units here -> total word gap 7 units.
|
||||||
|
case ' ':
|
||||||
|
delay(unitLength * 4);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Unsupported chars are ignored without adding an artificial gap.
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inter-character gap: 3 units total.
|
||||||
|
// dit()/dah() already trail 1 unit OFF, so add the remaining 2 units.
|
||||||
|
if (symbolSent) {
|
||||||
|
delay(unitLength * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
digitalWrite(LED_P, LOW);
|
||||||
|
digitalWrite(LED_N, LOW);
|
||||||
|
}
|
||||||
@ -183,8 +183,8 @@
|
|||||||
// theoretical you can use up to 3 LED. For example you can use it for a
|
// 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
|
// landmark beacon. So if you point this LEDs to different directions you can
|
||||||
// detect the position in dependence of your location to the bacon.
|
// detect the position in dependence of your location to the bacon.
|
||||||
#define LED1_N_SIDE 4
|
#define LED1_N_SIDE 3
|
||||||
#define LED1_P_SIDE 3
|
#define LED1_P_SIDE 4
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// Variables
|
// Variables
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Submodule hardware/0001_FireFly-Solar/.history updated: 7b062f08fe...1c8d4d7c5c
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
5
hardware/0001_FireFly-Solar/0001_FireFly-Solar.kicad_dru
Normal file
5
hardware/0001_FireFly-Solar/0001_FireFly-Solar.kicad_dru
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
(version 1)
|
||||||
|
(rule Minimum_text_height_and_thickness_1
|
||||||
|
(constraint text_height (min 0.6mm))
|
||||||
|
(constraint text_thickness (min 0.1mm))
|
||||||
|
)
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"board": {
|
"board": {
|
||||||
"active_layer": 25,
|
"active_layer": 5,
|
||||||
"active_layer_preset": "",
|
"active_layer_preset": "",
|
||||||
"auto_track_width": true,
|
"auto_track_width": true,
|
||||||
"hidden_netclasses": [],
|
"hidden_netclasses": [],
|
||||||
|
|||||||
@ -1,6 +1,349 @@
|
|||||||
{
|
{
|
||||||
"board": {
|
"board": {
|
||||||
"3dviewports": [],
|
"3dviewports": [],
|
||||||
|
"design_settings": {
|
||||||
|
"defaults": {
|
||||||
|
"apply_defaults_to_fp_barcodes": false,
|
||||||
|
"apply_defaults_to_fp_dimensions": false,
|
||||||
|
"apply_defaults_to_fp_fields": false,
|
||||||
|
"apply_defaults_to_fp_shapes": false,
|
||||||
|
"apply_defaults_to_fp_text": false,
|
||||||
|
"board_outline_line_width": 0.05,
|
||||||
|
"copper_line_width": 0.2,
|
||||||
|
"copper_text_italic": false,
|
||||||
|
"copper_text_size_h": 1.5,
|
||||||
|
"copper_text_size_v": 1.5,
|
||||||
|
"copper_text_thickness": 0.3,
|
||||||
|
"copper_text_upright": false,
|
||||||
|
"courtyard_line_width": 0.05,
|
||||||
|
"dimension_precision": 4,
|
||||||
|
"dimension_units": 3,
|
||||||
|
"dimensions": {
|
||||||
|
"arrow_length": 1270000,
|
||||||
|
"extension_offset": 500000,
|
||||||
|
"keep_text_aligned": true,
|
||||||
|
"suppress_zeroes": true,
|
||||||
|
"text_position": 0,
|
||||||
|
"units_format": 0
|
||||||
|
},
|
||||||
|
"fab_line_width": 0.1,
|
||||||
|
"fab_text_italic": false,
|
||||||
|
"fab_text_size_h": 1.0,
|
||||||
|
"fab_text_size_v": 1.0,
|
||||||
|
"fab_text_thickness": 0.15,
|
||||||
|
"fab_text_upright": false,
|
||||||
|
"other_line_width": 0.1,
|
||||||
|
"other_text_italic": false,
|
||||||
|
"other_text_size_h": 1.0,
|
||||||
|
"other_text_size_v": 1.0,
|
||||||
|
"other_text_thickness": 0.15,
|
||||||
|
"other_text_upright": false,
|
||||||
|
"pads": {
|
||||||
|
"drill": 0.7,
|
||||||
|
"height": 1.4,
|
||||||
|
"width": 1.4
|
||||||
|
},
|
||||||
|
"silk_line_width": 0.1,
|
||||||
|
"silk_text_italic": false,
|
||||||
|
"silk_text_size_h": 1.0,
|
||||||
|
"silk_text_size_v": 1.0,
|
||||||
|
"silk_text_thickness": 0.1,
|
||||||
|
"silk_text_upright": false,
|
||||||
|
"zones": {
|
||||||
|
"border_display_style": 2,
|
||||||
|
"border_hatch_pitch": 0.5,
|
||||||
|
"corner_radius": 0.0,
|
||||||
|
"corner_smoothing": 0,
|
||||||
|
"fill_mode": 0,
|
||||||
|
"hatch_gap": 1.5,
|
||||||
|
"hatch_orientation": 0.0,
|
||||||
|
"hatch_smoothing_level": 0,
|
||||||
|
"hatch_smoothing_value": 0.1,
|
||||||
|
"hatch_thickness": 1.0,
|
||||||
|
"min_clearance": 0.5,
|
||||||
|
"min_island_area": 10.0,
|
||||||
|
"min_thickness": 0.25,
|
||||||
|
"pad_connection": 1,
|
||||||
|
"remove_islands": 0,
|
||||||
|
"thermal_relief_gap": 0.5,
|
||||||
|
"thermal_relief_spoke_width": 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"diff_pair_dimensions": [
|
||||||
|
{
|
||||||
|
"gap": 0.0,
|
||||||
|
"via_gap": 0.0,
|
||||||
|
"width": 0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"drc_exclusions": [
|
||||||
|
[
|
||||||
|
"copper_edge_clearance|131952996|49276030|d5a8d279-1277-4128-8111-479753d97832|74ba0eb0-a42b-49db-8990-e7e337244d89",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"copper_edge_clearance|135254996|49276021|d5a8d279-1277-4128-8111-479753d97832|6760cf2b-9d51-44e9-8541-9b08cc6bed3a",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"copper_edge_clearance|138556996|49276011|d5a8d279-1277-4128-8111-479753d97832|5b16be99-1a09-4cc0-a4d6-0e5fae921e96",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_height|141224000|109999000|18cc7c24-1e1e-47b4-bd81-2abaec8c3831|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|129032000|119126000|be078bc2-b3be-4c33-abfe-930461332b78|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|129032000|122936000|57275588-b94d-44a7-b263-ab2e942abbb9|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|129032000|126746000|ac73bb6c-6b4c-4683-bc6b-c539afa6834f|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|129032000|130556000|dfc1c2d5-221c-45bf-8f4f-92d8a3ddf04a|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|129032000|134366000|e9026731-8c2e-491a-9b73-4cda2fa80a02|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|129032000|138176000|cf4eeb69-e06f-463f-be45-22626ac2c2b4|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|129032000|141986000|c29774df-bdb9-44e2-a17d-cfa3cd693d71|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|129184400|150241000|88401ad0-1dea-4344-ab33-329fd06bd493|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|135148000|106152000|7c4ccd6a-1b52-4734-bd95-092ada163852|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|135382000|84709000|88b0a397-e4a2-4145-86f6-0fa60b4a4bd9|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|141224000|109999000|18cc7c24-1e1e-47b4-bd81-2abaec8c3831|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|141325600|150144800|5da7a07f-f063-43b3-a149-e672965b459b|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|141478000|119126000|40b0fefb-8b86-43f7-b24d-e20c4677b223|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|141478000|122936000|77ee43fb-9700-42ab-8702-560042b5edd3|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|141478000|126746000|833d2a60-9010-4cb2-8045-b8050c8ee771|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|141478000|130556000|5a756363-e24d-4c12-82cf-59fc84a986ae|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|141478000|134366000|685c5797-de13-4bd0-a3b9-5fb75b4b9dad|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|141478000|138176000|15931c23-e5f6-4be3-ba3e-7a720c730e9b|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"text_thickness|141478000|141986000|e240b788-8dbb-43c9-8c9f-717e069201be|00000000-0000-0000-0000-000000000000",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"rule_severities": {
|
||||||
|
"annular_width": "error",
|
||||||
|
"clearance": "error",
|
||||||
|
"connection_width": "warning",
|
||||||
|
"copper_edge_clearance": "error",
|
||||||
|
"copper_sliver": "warning",
|
||||||
|
"courtyards_overlap": "error",
|
||||||
|
"creepage": "error",
|
||||||
|
"diff_pair_gap_out_of_range": "error",
|
||||||
|
"diff_pair_uncoupled_length_too_long": "error",
|
||||||
|
"drill_out_of_range": "error",
|
||||||
|
"duplicate_footprints": "warning",
|
||||||
|
"extra_footprint": "warning",
|
||||||
|
"footprint": "error",
|
||||||
|
"footprint_filters_mismatch": "ignore",
|
||||||
|
"footprint_symbol_field_mismatch": "warning",
|
||||||
|
"footprint_symbol_mismatch": "warning",
|
||||||
|
"footprint_type_mismatch": "ignore",
|
||||||
|
"hole_clearance": "error",
|
||||||
|
"hole_to_hole": "warning",
|
||||||
|
"holes_co_located": "warning",
|
||||||
|
"invalid_outline": "error",
|
||||||
|
"isolated_copper": "warning",
|
||||||
|
"item_on_disabled_layer": "error",
|
||||||
|
"items_not_allowed": "error",
|
||||||
|
"length_out_of_range": "error",
|
||||||
|
"lib_footprint_issues": "warning",
|
||||||
|
"lib_footprint_mismatch": "warning",
|
||||||
|
"malformed_courtyard": "error",
|
||||||
|
"microvia_drill_out_of_range": "error",
|
||||||
|
"mirrored_text_on_front_layer": "warning",
|
||||||
|
"missing_courtyard": "ignore",
|
||||||
|
"missing_footprint": "warning",
|
||||||
|
"missing_tuning_profile": "warning",
|
||||||
|
"net_conflict": "warning",
|
||||||
|
"nonmirrored_text_on_back_layer": "warning",
|
||||||
|
"npth_inside_courtyard": "error",
|
||||||
|
"padstack": "warning",
|
||||||
|
"pth_inside_courtyard": "error",
|
||||||
|
"shorting_items": "error",
|
||||||
|
"silk_edge_clearance": "ignore",
|
||||||
|
"silk_over_copper": "warning",
|
||||||
|
"silk_overlap": "ignore",
|
||||||
|
"skew_out_of_range": "error",
|
||||||
|
"solder_mask_bridge": "error",
|
||||||
|
"starved_thermal": "ignore",
|
||||||
|
"text_height": "warning",
|
||||||
|
"text_on_edge_cuts": "error",
|
||||||
|
"text_thickness": "warning",
|
||||||
|
"through_hole_pad_without_hole": "error",
|
||||||
|
"too_many_vias": "error",
|
||||||
|
"track_angle": "error",
|
||||||
|
"track_dangling": "warning",
|
||||||
|
"track_not_centered_on_via": "ignore",
|
||||||
|
"track_on_post_machined_layer": "error",
|
||||||
|
"track_segment_length": "error",
|
||||||
|
"track_width": "error",
|
||||||
|
"tracks_crossing": "error",
|
||||||
|
"tuning_profile_track_geometries": "ignore",
|
||||||
|
"unconnected_items": "error",
|
||||||
|
"unresolved_variable": "error",
|
||||||
|
"via_dangling": "warning",
|
||||||
|
"zones_intersect": "error"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"max_error": 0.005,
|
||||||
|
"min_clearance": 0.0,
|
||||||
|
"min_connection": 0.0,
|
||||||
|
"min_copper_edge_clearance": 0.5,
|
||||||
|
"min_groove_width": 0.0,
|
||||||
|
"min_hole_clearance": 0.25,
|
||||||
|
"min_hole_to_hole": 0.25,
|
||||||
|
"min_microvia_diameter": 0.2,
|
||||||
|
"min_microvia_drill": 0.1,
|
||||||
|
"min_resolved_spokes": 2,
|
||||||
|
"min_silk_clearance": 0.0,
|
||||||
|
"min_text_height": 0.8,
|
||||||
|
"min_text_thickness": 0.08,
|
||||||
|
"min_through_hole_diameter": 0.3,
|
||||||
|
"min_track_width": 0.2,
|
||||||
|
"min_via_annular_width": 0.1,
|
||||||
|
"min_via_diameter": 0.5,
|
||||||
|
"solder_mask_to_copper_clearance": 0.0,
|
||||||
|
"use_height_for_length_calcs": true
|
||||||
|
},
|
||||||
|
"teardrop_options": [
|
||||||
|
{
|
||||||
|
"td_onpthpad": true,
|
||||||
|
"td_onroundshapesonly": false,
|
||||||
|
"td_onsmdpad": true,
|
||||||
|
"td_ontrackend": false,
|
||||||
|
"td_onvia": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"teardrop_parameters": [
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_target_name": "td_round_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_target_name": "td_rect_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_target_name": "td_track_end",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"track_widths": [
|
||||||
|
0.0,
|
||||||
|
0.127,
|
||||||
|
0.254,
|
||||||
|
0.508,
|
||||||
|
0.635,
|
||||||
|
1.27
|
||||||
|
],
|
||||||
|
"tuning_pattern_settings": {
|
||||||
|
"diff_pair_defaults": {
|
||||||
|
"corner_radius_percentage": 80,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.2,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 1.0
|
||||||
|
},
|
||||||
|
"diff_pair_skew_defaults": {
|
||||||
|
"corner_radius_percentage": 80,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.2,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 0.6
|
||||||
|
},
|
||||||
|
"single_track_defaults": {
|
||||||
|
"corner_radius_percentage": 80,
|
||||||
|
"corner_style": 1,
|
||||||
|
"max_amplitude": 1.0,
|
||||||
|
"min_amplitude": 0.2,
|
||||||
|
"single_sided": false,
|
||||||
|
"spacing": 0.6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"via_dimensions": [
|
||||||
|
{
|
||||||
|
"diameter": 0.0,
|
||||||
|
"drill": 0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"zones_allow_external_fillets": false
|
||||||
|
},
|
||||||
"ipc2581": {
|
"ipc2581": {
|
||||||
"bom_rev": "",
|
"bom_rev": "",
|
||||||
"dist": "",
|
"dist": "",
|
||||||
@ -8,7 +351,7 @@
|
|||||||
"internal_id": "",
|
"internal_id": "",
|
||||||
"mfg": "",
|
"mfg": "",
|
||||||
"mpn": "",
|
"mpn": "",
|
||||||
"sch_revision": "0"
|
"sch_revision": "1"
|
||||||
},
|
},
|
||||||
"layer_pairs": [],
|
"layer_pairs": [],
|
||||||
"layer_presets": [],
|
"layer_presets": [],
|
||||||
@ -416,7 +759,7 @@
|
|||||||
"uuid": "d6486f66-f0ec-4484-bce4-0ec6b0e57e1d"
|
"uuid": "d6486f66-f0ec-4484-bce4-0ec6b0e57e1d"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"used_designators": "#FLG1-2,R2-3,J4,#PWR3-6,D5-6,C2-6,L1,U1-2",
|
"used_designators": "J1-2",
|
||||||
"variants": []
|
"variants": []
|
||||||
},
|
},
|
||||||
"sheets": [
|
"sheets": [
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
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