| Nume: | Alexandru Diaconu |
|---|---|
| Grupa: | 331CD |
Ce face?
CyberPull este un joc arcade interactiv pentru doi jucători inspirat din “trage de sfoară”. Un punct luminos pornește din centrul unei benzi LED. Fiecare jucător apasă butonul său cât mai repede pentru a trage punctul în jumătatea lui. Primul care îl duce la capătul său câștigă runda.
Care este scopul lui?
Distracție pentru doi jucători, testând viteza de reacție într-un format competitiv și vizual atractiv.
Care a fost ideea de la care am pornit?
Am vrut un proiect care să folosească toate conceptele de laborator (întreruperi, timere, PWM, ADC, I2C) într-un mod practic și interactiv, rezultând ceva ce poate fi demonstrat imediat oricui.
De ce este util?
Este un joc standalone complet funcțional, cu carcasă fizică din plexiglass, care poate fi folosit fără laptop după ce codul este încărcat.
Schema bloc a proiectului:
Modulele principale:
Listă de componente:
| Componentă | Specificații | Cantitate |
|---|---|---|
| Arduino Uno R3 | ATmega328P, DIP | 1 |
| Bandă LED WS2812B | 60 LED/m, 30 LED-uri (50cm) | 1 |
| Buton Arcade cu LED | 16mm, 3-6V, roșu | 1 |
| Buton Arcade cu LED | 16mm, 3-6V, albastru | 1 |
| Potențiometru | 10kΩ liniar | 1 |
| Buzzer Pasiv | 3.3V | 1 |
| LCD 1602 + modul I2C | PCF8574, 5V | 1 |
| Adaptor alimentare | 9V / 1A | 1 |
| Breadboard | 830 puncte | 1 |
| Rezistor 330Ω | 1/4W THT | 3 |
| Rezistor 10kΩ | 1/4W THT | 5 |
| Rezistor 100Ω | 1/4W THT | 3 |
| Condensator electrolitic | 1000µF / 35V | 1 |
| Fire Dupont M-F | 30cm, 40 buc | 1 set |
| Fire Dupont M-M | 10cm, 40 buc | 1 set |
Schema electrică:
Mediu de dezvoltare: Arduino IDE 2.x
Biblioteci folosite:
Adafruit_NeoPixel — control bandă LED WS2812BLiquidCrystal_I2C — control LCD prin I2Cavr/interrupt.h — întreruperi hardware (built-in)Concepte de laborator:
Aveti link catre youtube mai jos:
Cea mai delicată parte a fost lucratul cu banda LED și butoanele. Mai exact partea hardware, în care a trebuit să lipesc firele la pinii corespunzători de mai multe ori, deoarece legăturile s-au rupt între ele.
De asemenea, LCD-ul nu afișa nimic la prima pornire, deși adresa I2C era corectă (0x27). Problema era contrastul setat la zero din fabrică, rezolvat prin reglarea potențiometrului de pe spatele modulului.
| Dată | Activitate |
|---|---|
| 1 Mai 2026 | Definirea conceptului, finalizarea listei de componente |
| 4 Mai 2026 | Primirea componentelor și asamblarea hardware |
| 5 Mai 2026 | Implementare software — întreruperi + control LED |
| 6 Mai 2026 | Implementare software — ADC, PWM, I2C |
| 10 Mai 2026 | Testare și debugging |
| 25 Mai 2026 | Demo final și documentație |
Mediu de dezvoltare
main.cppBiblioteci third-party
| Bibliotecă | Rol |
|---|---|
| Adafruit NeoPixel | Controlul benzii LED WS2812B adresabile |
| LiquidCrystal_I2C | Comunicare cu LCD 16×2 prin protocolul I2C |
| avr/interrupt.h | Gestionarea întreruperilor hardware INT0/INT1 (built-in AVR) |
| Wire.h | Suport I2C pentru comunicarea cu LCD (built-in Arduino) |
Arhitectura sistemului
Firmware-ul este structurat într-un singur fișier principal main.cpp. Acesta inițializează toate perifericele (bandă LED, LCD, butoane, buzzer, potențiometru), configurează întreruperile hardware și gestionează logica principală în bucla loop().
Pinout-ul complet:
| Pin Arduino | Componentă | Rol |
|---|---|---|
| D2 (INT0) | Buton J2 (alb) | Întrerupere externă — apăsare jucător 2 |
| D3 (INT1) | Buton J1 (roșu) | Întrerupere externă — apăsare jucător 1 |
| D4 | LED buton J2 (alb) | Iluminare buton jucător 2 |
| D5 | LED buton J1 (roșu) | Iluminare buton jucător 1 |
| D6 | Bandă LED WS2812B (DIN) | Semnal date bandă LED (prin rezistență 330Ω) |
| D11 (OC2A) | Buzzer pasiv | Timer2 CTC — generare sunete |
| A0 | Potențiometru 10kΩ | ADC — citire dificultate 1–8 |
| A4 (SDA) | LCD I2C | Linie date I2C |
| A5 (SCL) | LCD I2C | Linie clock I2C |
Detalii cheie de implementare
Întreruperi hardware pentru butoane (Lab 2)
Butoanele jucătorilor sunt conectate pe pinii INT0 (D2) și INT1 (D3), configurate pe front descrescător (falling edge) prin registrele EICRA și EIMSK. La fiecare apăsare se declanșează automat rutinele ISR(INT0_vect) și ISR(INT1_vect). Aceasta garantează că nicio apăsare nu este pierdută, indiferent de ce face programul în acel moment.
Fiecare ISR implementează debouncing software cu o fereastră de 150ms — dacă două apăsări vin în mai puțin de 150ms, a doua este ignorată (zgomot mecanic al butonului). ISR-ul verifică și starea fizică a butonului cu digitalRead() pentru a ignora bouncing-ul la eliberare.
Logica de mutare a LED-ului:
Control buzzer prin Timer2 CTC (Lab 3)
Buzzerul pasiv este controlat de Timer2 în modul CTC pe pinul D11 (OC2A). Frecvența sunetului se calculează după formula:
OCR2A = F_CPU / (2 × 256 × frecvență) − 1
Prescaler-ul folosit este 256. Sunete generate:
Citire potențiometru prin ADC (Lab 4)
Potențiometrul este citit pe pinul A0 folosind analogRead(). Valoarea de 10 biți (0–1023) este mapată la intervalul de dificultate 1–8 folosind funcția map():
difficulty = map(analogRead(A0), 0, 1023, 1, 8);
Dificultatea reprezintă numărul de apăsări necesare pentru a muta LED-ul o poziție. La dificultate 1 fiecare apăsare mută LED-ul, la dificultate 8 sunt necesare 8 apăsări. Valoarea este recitită la fiecare iterație a loop(), deci se poate modifica în timp real.
Comunicare LCD prin I2C (Lab 6)
LCD-ul 16×2 cu modulul PCF8574 comunică prin protocolul I2C pe pinii A4 (SDA) și A5 (SCL), la adresa 0x27. Display-ul se actualizează la fiecare 500ms cu scorul curent, timpul scurs din rundă și dificultatea curentă.
Funcții implementate
| Funcție | Rol |
|---|---|
setup() | Inițializează toate perifericele, configurează întreruperile, pornește jocul |
loop() | Bucla principală: citește ADC, actualizează LED și LCD, verifică câștig |
ISR(INT0_vect) | Tratează apăsarea butonului alb (J2) — debounce 150ms + mutare LED spre dreapta |
ISR(INT1_vect) | Tratează apăsarea butonului roșu (J1) — debounce 150ms + mutare LED spre stânga |
showStrip() | Actualizează banda LED: zona J1 albastru, zona J2 roșu, bila albă |
flashStrip() | Clipire bandă LED în culoarea câștigătorului |
startAnimation() | Animație sweep alb la pornirea jocului |
buzzerOn() | Pornește buzzerul la frecvența specificată prin Timer2 CTC |
buzzerOff() | Oprește buzzerul |
playTone() | Emite un sunet la frecvența și durata specificate |
playMoveJ1Sound() | Sunet la mutarea LED-ului spre J1 (400Hz) |
playMoveJ2Sound() | Sunet la mutarea LED-ului spre J2 (600Hz) |
playRoundStart() | Sunet de start rundă: 300→500→700Hz |
playRoundWin() | Sunet la câștigarea unei runde |
playVictory() | Redă melodia Do-Re-Mi-Fa-Sol la câștigarea meciului |
updateLCDScore() | Afișează scorul, cronometrul și dificultatea pe LCD prin I2C |
checkWin() | Verifică dacă bila a ajuns la capătul benzii și actualizează scorul |
startRound() | Resetează pozițiile și contoarele pentru o rundă nouă |
checkReset() | Verifică dacă ambele butoane sunt ținute 500ms pentru reset joc |