This is an old revision of the document!
Proiectul “Bomb Defusal Challenge” este un joc fizic interactiv inspirat de “Keep Talking and Nobody Explodes”.
Jucătorii trebuie să dezamorseze o bombă virtuală prin rezolvarea mai multor mini-jocuri (module) înainte ca timerul să ajungă la zero.
Ceea ce face acest proiect special este integrarea hardware-ului real - senzori, LED-uri, butoane și afișaje - pentru a crea o experiență tactilă autentică.
Scopul proiectului este de a crea o experiență de joc cooperativă, care necesită comunicare clară, logică și abilitatea de a lucra sub presiune. Ideea de bază este de a transforma un joc digital popular într-o experiență fizică interactivă folosind componente electronice accesibile.
Acest proiect este util ca exercițiu practic de electronică și programare, demonstrând cum pot fi integrate multiple tehnologii într-un singur sistem coerent. Pentru jucători, oferă o experiență de joc unică și antrenantă care combină elemente digitale cu cele fizice.
Componente principale:
Unitatea centrală de control (ESP32)
- Gestionează logica jocului, timerele și comunicarea între module - Afișează informații pe ecranul TTGO T-Display - Generează sunetele pentru alarmă și feedback
Modulul Giroscop/Accelerometru (MPU-6500) compatibil I2C
- Detectează orientarea și mișcarea “bombei” - Permite activarea “gimmick-ului” care necesită întoarcerea bombei când alarma sună
Modulul Simon Says Go
- Semaforul cu 3 LED-uri (roșu, galben, verde) - LED-ul “Simon” care indică când trebuie apăsat un buton - 3 butoane tactile pentru interacțiunea jucătorului
Modulul Wire Cutting
- 3 conexiuni de fire detașabile - Sistem de detecție pentru identificarea firului “tăiat” - Logică pentru determinarea firului corect bazată pe alte condiții
Modulul Bomb Disarm Code (Beta)
- Senzor de presiune BMP180 compatibil I2C utilizat ca “fingerprint scanner” - Senzor de temperatură DS18B20 pentru măsurarea căldurii degetului - Senzor de sunet pentru detectarea parolei vocale șoptite/fluierate - LED-uri pentru feedback vizual
Sistem de alarmă și feedback
- Modul amplificator audio LM386 pentru alarmă și efecte sonore - LEDuri pentru indicarea statusului (timpul rămas, greșeli)
Toate aceste module comunică cu unitatea centrală ESP32, care coordonează logica jocului și afișează informații relevante pe ecran.
| Nume | Descriere |
|---|---|
| Placă TTGO T-Display ESP32 | Controler pentru module și unitatea centrală |
| Modul MPU-6500 (accelerometru + giroscop) | Detectarea orientării bombei |
| Modul senzor sunet LM393 | Detectarea parolei vocale |
| Senzor BMP180 | Măsurarea presiunii pentru “fingerprint scanner” |
| Senzor DS18B20 | Măsurarea temperaturii degetului |
| Modul amplificator audio LM386 | Sistem de alarmă și efecte sonore |
| Conectori XH2.54 | Conexiuni pentru module |
| Module cu 3 LED-uri (R, Y, G) | Semafor pentru Simon Says și indicatori |
| Switch-uri toggle | Controale și interacțiuni |
| Senzor distanță HC-SR04P | - |
| LED-uri verzi 5mm | Indicatori de stare |
| LED-uri galbene 5mm | Indicatori de stare |
| LED-uri roșii 3mm | Indicatori de eroare |
| Butoane rotunde | Interfața pentru interacțiuni |
| Condensatoare + Rezistoare (diverse) | Filtrare și stabilizare circuite |
| Mini breadboarduri | Montarea circuitelor |
| Fire de conexiune | Conectarea componentelor |
Deignuit pe Platformio: VSCode.
Biblioteci:
* driver/dac.h - pt sunet
* TFT_eSPI.h - pt display
* Arduino.h - pt ft putine lucruri (setup)
Timer loop:
Timer loopul se triggeruieste la fiecare tick, si mentine starea jocului prin urmatorul loop:
void IRAM_ATTR onTimer() {
portENTER_CRITICAL_ISR(&timerMux);
myMillis++;
if (myMillis % 200 == 0) {
if (is_active(GAME_SIMON_SAYS)) {
if (simon_can_do_next_round){
simon_last_round_begin_milis = myMillis;
simon_can_do_next_round = false;
simon_round_ongoing = true;
}
int deltaMillisTilStart = (myMillis - simon_last_round_begin_milis);
if ((myMillis - simon_last_round_begin_milis) <= simon_round_intro_time_delay_milis) {
// round pending
if (deltaMillisTilStart % 1000 == 0)
simonOnRoundPrepareTick((simon_round_intro_time_delay_milis - deltaMillisTilStart) / 1000);
} else {
if (!simon_semaphore_started){
simonOnRoundStart();
simon_semaphore_started = true;
}
int time_since_round_semaphore_start = deltaMillisTilStart - simon_round_intro_time_delay_milis;
int tick_cnt = time_since_round_semaphore_start / simon_time_delay_between_colors;
if (time_since_round_semaphore_start % simon_time_delay_between_colors == 0) {
if (tick_cnt <= simon_size_of_colors)
simonOnRoundStartTick(tick_cnt);
else {
if (!simon_can_begin_pressing){
simonOnGameEnd();
}
simon_can_begin_pressing = true;
}
}
}
} else if (is_active(GAME_WIRE_CUTTING)) {
if (!wire_cut_game_started){
wireCutOnStart();
wire_cut_game_started = true;
wire_cut_game_start_time = myMillis;
}
if (wire_cut_game_started) {
int deltaMilisSinceWireCutStart = (myMillis - wire_cut_game_start_time);
if (deltaMilisSinceWireCutStart % 1000 == 0) {
int ticks = deltaMilisSinceWireCutStart / 1000;
if (ticks < wire_cut_game_duration_seconds)
wireCutOnGameTick(wire_cut_game_duration_seconds - ticks);
else if (!wire_cut_game_ended) {
wire_cut_game_ended = true;
wire_cut_game_won = false;
}
}
}
} else if (is_active(GAME_PASS_UNLOCK)) {
if (!pass_game_started) {
pass_game_started = true;
pass_game_start_time = myMillis;
passUnlockOnStart();
}
if (pass_game_started) {
int dm = (myMillis - pass_game_start_time);
if (dm % 1000 == 0) {
int ticks = dm / 1000;
if (ticks < pass_game_time_limit_seconds)
passUnlockOnGameTick(pass_game_time_limit_seconds - ticks);
else if (!pass_game_ended) {
pass_game_ended = true;
pass_game_won = false;
}
}
}
}
}
portEXIT_CRITICAL_ISR(&timerMux);
}
* Fiecare callback este implemetat:
- Simon Games callbacks defns & vars:
int simon_size_of_colors = 3; char simonBufferSemaphoreSize = 0; char simonBufferCompareIdx = 0; void simonOnRoundPrepareTick(int tick_count); void simonOnRoundStart(); void simonOnRoundStartTick(int tick_count); void simonOnGameEnd();
- Wire Cut callbacks defns & vars:
volatile int wire_cut_game_duration_seconds = 20; volatile int wire_cut_game_target_wire = -1; void wireCutOnStart(); void wireCutOnGameTick(int ticks_left); void wireCutOnEnd(bool won);
- Finger scan callbacks defns & vars:
int pass_env_temperatures_read_interv_millis = 1000; // pt calibrarea temperaturii de mediu const int pass_env_temperatures_read_limit = 10; const float pass_temp_threshold_celsius = 0.5; volatile int pass_game_time_limit_seconds = 10; void passUnlockOnStart(); void passUnlockOnGameTick(int ticks_left); void passUnlockOnEnd(bool won);
! Singura folosire a delay-ului implementat nu de mine (custom) e in functia de playNote care ajuta la audio.
const int dacPin = 25; // DAC1 - GPIO25 const int frequency = 1000; // tone frequency Hz const int duration = 100; // duration in ms
void playTone(int freq, int dur_ticks=1) {
int samplesPerCycle = 100; // number of samples per waveform cycle
int sampleDelayUs = 1000000 / (freq * samplesPerCycle);
int amplitude = 64; // 8-bit DAC mid-range (0-255)
int dur_ms = dur_ticks * duration;
unsigned long endTime = millis() + dur_ms;
while (millis() < endTime) {
for (int i = 0; i < samplesPerCycle; i++) {
// Generate a sine wave sample (scaled 0-255)
float sineVal = sin(2 * PI * i / samplesPerCycle);
uint8_t dacVal = (uint8_t)(amplitude + amplitude * sineVal);
dac_output_voltage(DAC_CHANNEL_1, dacVal);
delayMicroseconds(sampleDelayUs);
}
}
dac_output_voltage(DAC_CHANNEL_1, 128); // center output (silence)
}
care nu e inclus in cele 3 laboratoare.
I2C ul se face pe pinii 26 si 27, pt ca pinul 25 e folosit pt DAC. Acesta e conectat atat la senzorl giroscopic cat si la cel de temperatura.
Senzorii de giroscop si temperatura sunt implementati cu drivere programate de mine dupa datasheets.
Fişierele se încarcă pe wiki folosind facilitatea Add Images or other files. Namespace-ul în care se încarcă fişierele este de tipul :pm:prj20??:c? sau :pm:prj20??:c?:nume_student (dacă este cazul). Exemplu: Dumitru Alin, 331CC → :pm:prj2009:cc:dumitru_alin.
Laburi folosite:
- GPIO
- Timere (flow-ul jocului)
- Intreruperi (butoane/fire)
- I2C (senzorii de temp si de giroscop comunica in tandem pe magistrala SDA SCL conectate la Pinii 25 si 26)