Proiectul constă într-un sistem interactiv bazat pe Arduino, capabil să analizeze și să afișeze spectrul frecvențelor audio în timp real pe o matrice de LED-uri (8×32). Sistemul captează semnalul acustic prin intermediul unui microfon analogic și extrage componentele de frecvență utilizând transformata Fourier rapidă (FFT). Datele procesate sunt transmise către display prin interfața SPI, garantând un refresh rate optim pentru fluiditatea animației.
Când nu este folosit pentru muzică, dispozitivul poate fi trecut într-un mod de așteptare (Standby), unde funcționează ca un ceas digital și termometru, oferind informații despre mediul ambiant. Comutarea între funcția de analiză audio și cea ambientală se face simplu, prin acționarea unui buton fizic (declanșând o întrerupere sau o schimbare de stare).
Dispozitivul are trei moduri de functionare intre care utilizatorul comuta cu un singur buton:
Comutarea intre moduri se face printr-o singura apasare de buton, secvential. La pornire dispozitivul intra implicit in modul IDLE.
| Componenta | Rol in proiect |
|---|---|
| Arduino UNO R3 | Microcontroller principal (ATmega328P) |
| MAX7219 (4x8x8) | Driver pentru matricea LED 8×32 |
| LM386 | Modul microfon cu amplificator (iesire analog) |
| DHT11 | Senzor de temperatura ambientala |
| DS1302 | RTC (Real-Time Clock) pentru ora curenta |
| Buton | Comuta intre cele doua moduri de afisare |
Mai jos sunt detaliate toate conexiunile dintre Arduino UNO si modulele externe. Toate componentele sunt alimentate la 5V si impart aceeasi masa (GND) cu placa Arduino.
| Pin modul | Pin Arduino | Functie |
|---|---|---|
| DIN | D11 (MOSI) | Date seriale spre LED |
| CLK | D13 (SCK) | Ceas SPI |
| CS | D10 | Chip Select |
| VCC | 5V | Alimentare |
| GND | GND | Masa |
| Pin modul | Pin Arduino | Functie |
|---|---|---|
| AOUT | A0 | Semnal audio amplificat (ADC) |
| VCC | 5V | Alimentare |
| GND | GND | Masa |
| Pin modul | Pin Arduino | Functie |
|---|---|---|
| CLK | D6 | Ceas serial |
| DAT (I/O) | D7 | Linie bidirectionala |
| RST (CE) | D8 | Chip Enable |
| VCC | 5V | Alimentare |
| GND | GND | Masa |
| Pin modul | Pin Arduino | Functie |
|---|---|---|
| DATA | D2 | Linie de date one-wire |
| VCC | 5V | Alimentare |
| GND | GND | Masa |
| Pin buton | Pin Arduino | Observatii |
|---|---|---|
| SIG | D5 | Folosit cu pull-up intern (INPUT_PULLUP) |
| GND | GND | Masa |
Pentru modul Spectru Audio am avut nevoie de control fin pe fiecare coloana a matricei LED, motiv pentru care am ales MD_MAX72xx. Ea permite scrierea directa a unui byte per coloana, ceea ce este exact ce iti trebuie atunci cand vrei sa desenezi bare verticale dinamice: fiecare coloana corespunde unei benzi de frecventa, iar inaltimea ei se obtine in urma analizei FFT.
Pentru analiza propriu-zisa a semnalului am folosit arduinoFFT. Aceasta primeste 64 de esantioane preluate pe pinul A0, le aplica o fereastra de tip Hamming pentru reducerea artefactelor de margine si calculeaza apoi transformata Fourier. Modulul fiecarei componente complexe rezultate da amplitudinea benzii respective de frecventa.
In modurile IDLE (ceas + temperatura), unde am nevoie sa afisez text si nu mai trebuie control la nivel de coloana, am folosit MD_Parola, care este construita peste MD_MAX72xx si simplifica mult afisarea de mesaje, fonturi si animatii.
Pentru a evita reconstruirea fiecarei bare la fiecare frame, am declarat un vector spectralHeight[] care contine cele 9 valori binare posibile (de la 0 la 8 LED-uri aprinse pe coloana). Astfel, dupa ce stiu inaltimea unei bare, scriu direct byte-ul corespunzator pe coloana respectiva.
double sum = 0; for (int i = 0; i < 64; i++) { realComponent[i] = analogRead(A0) / sensitivity; imagComponent[i] = 0; sum += realComponent[i]; } // Scad media (DC offset-ul microfonului) pentru a evita // artefactele de la primele bin-uri ale FFT-ului. double avg = sum / 64.0; for (int i = 0; i < 64; i++) { realComponent[i] -= avg; }
Microfonul LM386 are pe iesire un semnal centrat pe ~Vcc/2, deci ADC-ul vede o valoare medie in jur de 512 (la 10 biti). Daca lasi acel offset, FFT-ul il “vede” ca pe o componenta de frecventa 0 foarte mare si bin-ul 0 (si partial 1) devine permanent saturat. Scaderea mediei rezolva exact aceasta problema.
FFT.windowing(realComponent, 64, FFT_WIN_TYP_HAMMING, FFT_FORWARD); FFT.compute(realComponent, imagComponent, 64, FFT_FORWARD); FFT.complexToMagnitude(realComponent, imagComponent, 64); // Forteaza la zero bin-urile DC si quasi-DC, pentru siguranta. realComponent[0] = 0; realComponent[1] = 0;
windowing aplica fereastra Hamming peste cele 64 de esantioane (reduce artefactele de margine).compute face FFT-ul propriu-zis.complexToMagnitude calculeaza sqrt(Re^2 + Im^2) pentru fiecare bin, deci amplitudinea acelei frecvente.for (int i = 0; i < 32; i++) { realComponent[i] = constrain(realComponent[i], 0, 80); realComponent[i] = map(realComponent[i], 0, 80, 0, 8); disp.setColumn(31 - i, spectralHeight[(int)realComponent[i]]); }
constrain taie valori excesive (orice peste 80 e zgomot puternic si oricum satureaza vizual).map aduce amplitudinea in domeniul 0..8, adica exact numarul de LED-uri pe care le pot aprinde pe o coloana.setColumn(31 - i, …) deseneaza bara: indicele este inversat fiindca matricea este oglindita fizic fata de directia de citire a FFT-ului.spectralHeight[k] este un cod binar pre-calculat (de exemplu 0b11111100 aprinde 6 LED-uri din 8 de jos in sus).Proiectul functioneaza conform asteptarilor in ambele moduri. Matricea reactioneaza vizibil la muzica si voce, iar in modul IDLE ceasul si temperatura sunt afisate clar.
Initial am avut o problema vizibila in care primele doua coloane din matrice ramaneau aprinse chiar si in liniste totala (vezi imaginea de mai jos). Cauza era DC offset-ul ramas in semnalul ADC, care apare in FFT exact pe bin-urile 0 si 1. Dupa ce am scazut media semnalului inainte de FFT si am fortat la zero acele doua bin-uri, problema a disparut.
Pasul urmator este montarea finala intr-o carcasa care sa ascunda firele si sa dea proiectului un aspect mai prezentabil.
Per total, proiectul a fost in egala masura provocator si placut. Cea mai mare parte din timp a mers pe documentare - voiam sa fiu sigur ca nu ard ceva inainte de a alimenta vreun modul - dar in schimb partea efectiva de montaj a fost mai rapida decat mi-am imaginat.
Surpriza placuta a fost ca, desi am stat ore intregi cu firele si cu codul, mi-a placut procesul: de la debugging la rearanjarea cablurilor si la cititul de datasheet-uri. Pana acum credeam ca ma intereseaza doar partea de software, dar acum stiu ca imi place si latura de hardware a facultatii.
Posibile imbunatatiri pentru o versiune viitoare:
Proiectul ramane o baza buna pe care pot construi mai departe, iar acum cand am toata partea hardware si software stabila, iterarile viitoare vor fi mult mai rapide.