🧩 Code Structure
Štruktúra repozitára
ST-016-SMVIT/
├── content/docs/sk/sthdf/ST-016-OndrejSpanik/ ← táto dokumentácia (7Ds, SDLC, Q12, deliverables)
├── content/docs/assets/sthdf/ST-016-OndrejSpanik/
│ ├── img/ ← SVG ilustrácie (schémy, rezy, diagramy)
│ └── photos/ ← fotky z výroby (01-material … 10-final)
├── hardware/
│ ├── cad/ ← lelek.FCStd, lelek.step, button.FCStd
│ └── cam/ ← *.nc (G-kód) + poznámky k nástrojom a posuvom
├── firmware/lelek/ ← lelek.ino (ATtiny85)
├── audio/ ← zdrojová nahrávka + LICENSE.txt (xeno-canto)
└── sd-card/mp3/0001.mp3
Firmvér – firmware/lelek/lelek.ino
Celý firmvér má ≈ 120 riadkov. Kľúčové rozhodnutia:
- Bez SoftwareSerial – zaberá vektor
PCINT0, ktorý potrebujeme na prebudenie tlačidlom. UART je bit-bang, len TX, priamy zápis doPORTB. quietPins()pred spánkom – TX a BUSY sa prepnú na výstup LOW, inak by cez ochranné diódy vypnutého DFPlayera tiekli miliampéry.- Meranie VCC bez externých súčiastok – ADC meria internú 1,1 V referenciu voči VCC.
- Dva timeouty – ak DFPlayer nezačne hrať do 3 s alebo hrá dlhšie ako 30 s, napájanie sa vypne (ochrana článku pri chybnej karte).
// LELEK – firmvér pre ATtiny85 (ATTinyCore, 8 MHz interný RC, bez bootloadera)
// Strážca napájania: spí, prebudí sa tlačidlom, zapne DFPlayer cez P-MOSFET,
// pošle príkaz "hraj /mp3/0001.mp3", počká na koniec (BUSY) a všetko vypne.
//
// Piny (číslovanie Arduino = PBx):
// PB0 -> EN gate P-MOSFETu (LOW = DFPlayer zapnutý)
// PB1 <- BUSY z DFPlayera (LOW = hrá)
// PB2 -> TX do RX DFPlayera cez 1 kΩ (bit-bang UART 9600 Bd)
// PB3 <- BTN tlačidlo na GND, interný pull-up, PCINT3 budí zo spánku
// PB4 -> LED voliteľná LED + 1 kΩ
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
const uint8_t PIN_EN = 0;
const uint8_t PIN_BUSY = 1;
const uint8_t PIN_TX = 2;
const uint8_t PIN_BTN = 3;
const uint8_t PIN_LED = 4;
const uint8_t VOLUME = 22; // 0–30; nad ~25 klony skresľujú
const uint16_t TRACK = 1; // /mp3/0001.mp3
const uint16_t BOOT_MS = 2000; // čas na inicializáciu microSD
const uint16_t START_TO_MS = 3000; // BUSY musí klesnúť do 3 s od príkazu
const uint32_t PLAY_MAX_MS = 30000; // poistka: max. dĺžka prehrávania
const uint16_t VBAT_MIN_MV = 3300; // pod touto hodnotou nehráme
// ---------- bit-bang UART (len TX). SoftwareSerial nepoužívame – zaberá PCINT0
// vektor, ktorý potrebujeme na prebudenie tlačidlom. Priamy zápis do PORTB
// trvá 1 takt, takže časovanie bitov sedí.
static inline void txHigh() { PORTB |= _BV(PB2); }
static inline void txLow() { PORTB &= ~_BV(PB2); }
void uartByte(uint8_t b) {
const uint16_t BIT_US = 102; // 104 µs mínus réžia slučky
uint8_t oldSREG = SREG; cli();
txLow(); delayMicroseconds(BIT_US); // start bit
for (uint8_t i = 0; i < 8; i++) {
if (b & 1) txHigh(); else txLow();
b >>= 1;
delayMicroseconds(BIT_US);
}
txHigh(); delayMicroseconds(BIT_US); // stop bit
SREG = oldSREG;
}
void dfCommand(uint8_t cmd, uint16_t param) {
uint8_t f[10] = { 0x7E, 0xFF, 0x06, cmd, 0x00,
(uint8_t)(param >> 8), (uint8_t)(param & 0xFF), 0, 0, 0xEF };
uint16_t sum = 0;
for (uint8_t i = 1; i <= 6; i++) sum += f[i];
uint16_t cs = (uint16_t)(0 - sum);
f[7] = cs >> 8; f[8] = cs & 0xFF;
for (uint8_t i = 0; i < 10; i++) uartByte(f[i]);
}
// ---------- meranie napätia článku: 1,1 V referencia meraná voči VCC
uint16_t readVccMv() {
ADCSRA |= _BV(ADEN);
ADMUX = _BV(MUX3) | _BV(MUX2); // MUX[3:0] = 1100 -> Vbg
delay(2); // ustálenie referencie
ADCSRA |= _BV(ADSC); while (ADCSRA & _BV(ADSC));
uint16_t raw = ADC;
ADCSRA &= ~_BV(ADEN); // ADC vypnúť pred spánkom
return (uint32_t)1100UL * 1024UL / raw;
}
void blink(uint8_t n, uint16_t ms = 120) {
for (uint8_t i = 0; i < n; i++) {
digitalWrite(PIN_LED, HIGH); delay(ms);
digitalWrite(PIN_LED, LOW); delay(ms);
}
}
// ---------- pred spánkom: nič nesmie tiecť do vypnutého DFPlayera
void quietPins() {
pinMode(PIN_TX, OUTPUT); digitalWrite(PIN_TX, LOW); // inak 3 mA cez 1 kΩ + diódu
pinMode(PIN_BUSY, OUTPUT); digitalWrite(PIN_BUSY, LOW); // žiadny pull-up do modulu
digitalWrite(PIN_LED, LOW);
}
void sleepNow() {
quietPins();
GIMSK |= _BV(PCIE); // pin-change interrupt zapnúť
PCMSK |= _BV(PCINT3); // ... len pre PB3
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sei();
sleep_cpu(); // <-- tu spíme (≈ 1 µA)
sleep_disable();
PCMSK &= ~_BV(PCINT3);
}
ISR(PCINT0_vect) { /* stačí prebudiť */ }
void setup() {
wdt_disable();
pinMode(PIN_EN, OUTPUT); digitalWrite(PIN_EN, HIGH); // DFPlayer vypnutý
pinMode(PIN_BTN, INPUT_PULLUP);
pinMode(PIN_LED, OUTPUT);
quietPins();
blink(1); // "žijem"
}
void loop() {
sleepNow();
delay(30); // debounce
if (digitalRead(PIN_BTN) == HIGH) return; // falošné prebudenie
if (readVccMv() < VBAT_MIN_MV) { // slabý článok
blink(3);
while (digitalRead(PIN_BTN) == LOW) {}
return;
}
// zapnúť DFPlayer
pinMode(PIN_TX, OUTPUT); txHigh(); // UART idle = HIGH
pinMode(PIN_BUSY, INPUT); // teraz už čítame
digitalWrite(PIN_EN, LOW);
delay(BOOT_MS);
dfCommand(0x06, VOLUME); delay(50);
dfCommand(0x12, TRACK); // /mp3/0001.mp3
digitalWrite(PIN_LED, HIGH); delay(100); digitalWrite(PIN_LED, LOW);
uint32_t t0 = millis();
while (digitalRead(PIN_BUSY) == HIGH && millis() - t0 < START_TO_MS) {} // čakaj na štart
while (digitalRead(PIN_BUSY) == LOW && millis() - t0 < PLAY_MAX_MS) {} // čakaj na koniec
delay(100);
digitalWrite(PIN_EN, HIGH); // vypnúť DFPlayer
quietPins();
while (digitalRead(PIN_BTN) == LOW) {} // počkaj na pustenie tlačidla
}
Nastavenie Arduino IDE
- Nainštalovať ATTinyCore (Boards Manager URL:
http://drazzy.com/package_drazzy.com_index.json). - Board: ATtiny25/45/85 (No bootloader) · Chip: ATtiny85 · Clock: 8 MHz (internal) · B.O.D.: disabled (kvôli odberu) · millis: enabled.
- Programátor: USBasp (alebo Arduino as ISP). Najprv Burn Bootloader (nastaví poistky na 8 MHz), potom Upload Using Programmer.
- Po nahratí odpojiť programátor – jeho pull-upy by v spánku zvyšovali odber.
CAM – hardware/cam/
| Súbor | Operácia | Nástroj | Otáčky | Posuv | Stepdown |
|---|---|---|---|---|---|
A-pockets.nc, B-pockets.nc | 2.5D kapsy + kanály | valcová 6 mm, 2 brity | 18 000 /min | 1 200 mm/min | 3 mm (lipa) / 2 mm (buk) |
B-hatch.nc | osadenie poklopu, otvory pre magnety | valcová 6 mm, 3 mm | 18 000 | 1 000 | 2 mm |
body-rough.nc | 3D hrubovanie tela (po zlepení) | valcová 6 mm | 18 000 | 1 500 | 3 mm, stepover 50 % |
body-finish.nc | 3D dokončenie | guľová 3 mm | 20 000 | 1 200 | stepover 0,4 mm (paralelne s vláknom) |
grille.nc | zvuková mriežka | vrták / fréza 2 mm | 12 000 | 300 (peck) | 2 mm |
Hodnoty sú štartovacie pre hobby CNC (spindle 500 W+), overené na lipe; na buku znížiť posuv o 20–30 %.
Stav firmvéru (5. 10. 2026)
| Verzia | Firmvér |
|---|---|
| v3 | lelek_attiny85: spánok, MOSFET-spínaný DFPlayer, TX/BUSY LOW, cutoff 3,3 V |
| v4 | lelek_v4_dfplayer_pro: AT príkazy 115200 Bd, hranie na fixný čas |
| v5 | lelek_v5_attiny85_dfplayer_pro: holý čip, 8 MHz interný, pull-up PB3; prekladá sa (720 B), na hardvéri nespustené |
| v6, v7 | žiadny firmvér |