CyberPull: Tug of War Digital

Nume: Alexandru Diaconu
Grupa: 331CD

Introducere

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.

Descriere Generală

Schema bloc a proiectului:

Modulele principale:

  • Arduino Uno (ATmega328P) — unitatea centrală, rulează toată logica jocului
  • Bandă LED WS2812B (30 LED-uri, 50cm) — afișajul principal; un LED se mișcă stânga/dreapta
  • 2x Butoane Arcade 16mm cu LED — inputul jucătorilor, conectate pe INT0/INT1
  • Potențiometru 10kΩ — reglează dificultatea în timp real prin ADC
  • Buzzer Pasiv — feedback audio controlat prin PWM (Timer2)
  • LCD 1602 + I2C — afișează scorul și cronometrul rundei
  • Adaptor 9V/1A — alimentare standalone prin jack-ul Arduino

Hardware Design

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ă:

Software Design

Mediu de dezvoltare: Arduino IDE 2.x

Biblioteci folosite:

  • Adafruit_NeoPixel — control bandă LED WS2812B
  • LiquidCrystal_I2C — control LCD prin I2C
  • avr/interrupt.h — întreruperi hardware (built-in)

Concepte de laborator:

  • Lab 2 — Întreruperi: Butoanele conectate pe INT0 (D2) și INT1 (D3), tratate prin ISR. Timer1 în mod CTC pentru cronometrul rundei (tick 1ms). Debouncing software cu fereastră de 50ms.
  • Lab 3 — PWM: Timer2 în mod Fast PWM pe pinul D11 (OC2A) pentru buzzer. Frecvențe diferite: ~500Hz (mutare J1), ~800Hz (mutare J2), melodie la câștig.
  • Lab 4 — ADC: Potențiometrul citit pe A0. Valoarea 0–1023 mapată la dificultate 1–8 (numărul de apăsări necesare per mutare LED).
  • Lab 6 — I2C: LCD comunică pe pinii A4 (SDA) / A5 (SCL). Adresă PCF8574: 0x27. Actualizare la fiecare 500ms.

Rezultate Obținute

Aveti link catre youtube mai jos:

https://www.youtube.com/shorts/ELwuYHDpFSM

Concluzii

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.

Jurnal

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

Software Design

Mediu de dezvoltare

  • PlatformIO (extensie VS Code)
  • Placă: Arduino Uno (ATmega328P)
  • Limbaj: C++
  • Fișier principal: main.cpp

Biblioteci 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:

  • ISR numără apăsările într-un contor
  • Când contorul atinge valoarea de dificultate → LED-ul se mută o poziție
  • Contorul se resetează la 0

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:

  • 400 Hz — mutare spre J1
  • 600 Hz — mutare spre J2
  • 300→500→700 Hz — start rundă
  • Melodie Do-Re-Mi-Fa-Sol (262–392 Hz) — victorie meci

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

Bibliografie/Resurse

pm/prj2026/bianca.popa1106/alexandru.diaconu.txt · Last modified: 2026/05/27 03:37 by alexandru.diaconu04
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0